단지 콜백을 약속하지 않습니까?


430

몇 년 동안 JavaScript를 개발해 왔으며 약속에 대한 소란을 전혀 이해하지 못합니다.

내가하는 일은 변화하는 것 같습니다.

api(function(result){
    api2(function(result2){
        api3(function(result3){
             // do work
        });
    });
});

어쨌든 비동기 와 같은 라이브러리를 사용할 수 있습니다 .

api().then(function(result){
     api2().then(function(result2){
          api3().then(function(result3){
               // do work
          });
     });
});

코드가 많고 읽기 쉽지 않습니다. 나는 여기서 아무것도 얻지 못했습니다. 갑자기 마술처럼 '평평하지 않습니다'. 사물을 약속으로 바꾸어야한다는 것은 말할 것도 없습니다.

그렇다면 약속에 대한 큰 소란은 무엇입니까?


11
주제 : Html5Rocks에 대한 약속에 대한 실질적인 정보 기사가 있습니다. html5rocks.com/en/tutorials/es6/promises
ComFreek

2
참고로 당신이 받아 들인 대답은 약속의 포인트가 아니며 약속을 사용하도록 설득하지 않은 사소한 이점의 오래된 목록입니다. 약속을 사용하도록 설득 한 것은 Oscar의 답변에 설명 된 DSL 측면이었습니다
Esailija

@Esailija, 당신의 leet 연설은 저를 설득했습니다. Bergi의 답변이 실제로 좋은 점과 다른 점을 제기한다고 생각하지만 다른 대답을 받아 들였습니다.
Benjamin Gruenbaum

@Esailija "Oscar의 답변에서 설명한대로 약속을 사용하게 된 것은 DSL 측면이었습니다."<< "DSL"이란 무엇입니까? 그리고 당신이 말하는 "DSL 측면"은 무엇입니까?
monsto

1
@monsto : DSL : 도메인 특정 언어 (Domain Specific Language)는 시스템의 특정 하위 집합 (예 : 데이터베이스와 통신하기위한 SQL 또는 ORM, 패턴을 찾기위한 정규식 등)에서 사용하도록 의도 된 언어입니다. 이러한 맥락에서 "DSL"은 Promise의 API로, Oscar가하는 방식으로 코드를 구성하는 경우 비동기 작업의 특정 컨텍스트를 처리하기 위해 JavaScript를 보완하는 구문 설탕과 거의 같습니다. 약속은 프로그래머가 이러한 유형의 구조의 다소 어려운 정신적 흐름을보다 쉽게 ​​파악할 수 있도록 설계된 언어로 거의 전환하는 일부 숙어를 만듭니다.
Michael Ekoka

답변:


631

약속은 콜백이 아닙니다. 약속은 비동기 작업미래 결과를 나타냅니다 . 물론, 당신이하는 방식으로 글을 쓰면 거의 혜택을 얻지 못합니다. 그러나 사용하려는 방식으로 작성하면 동기 코드와 유사한 방식으로 비동기 코드를 작성할 수 있으며 훨씬 더 쉽게 수행 할 수 있습니다.

api().then(function(result){
    return api2();
}).then(function(result2){
    return api3();
}).then(function(result3){
     // do work
});

확실히 적은 코드는 아니지만 훨씬 더 읽기 쉽습니다.

그러나 이것은 끝이 아닙니다. 진정한 이점을 발견합시다 : 어떤 단계에서 오류를 확인하려면 어떻게해야합니까? 콜백으로 그것을하는 것은 지옥 일 것이지만 약속으로 케이크 한 조각입니다.

api().then(function(result){
    return api2();
}).then(function(result2){
    return api3();
}).then(function(result3){
     // do work
}).catch(function(error) {
     //handle any error that may occur before this point
});

try { ... } catch블록 과 거의 동일합니다 .

더 나은 :

api().then(function(result){
    return api2();
}).then(function(result2){
    return api3();
}).then(function(result3){
     // do work
}).catch(function(error) {
     //handle any error that may occur before this point
}).then(function() {
     //do something whether there was an error or not
     //like hiding an spinner if you were performing an AJAX request.
});

그리고 더 나은 : 그 3 호출하는 경우에는 어떻게 api, api2, api3동시에 실행할 수 있습니다 (예를 들어, 그들은 AJAX 호출 인 경우) 그러나 당신이 세 가지를 기다릴 필요? 약속 없이는 일종의 카운터를 만들어야합니다. 약속과 함께 ES6 표기법을 사용하는 또 다른 케이크 조각이며 매우 깔끔합니다.

Promise.all([api(), api2(), api3()]).then(function(result) {
    //do work. result is an array contains the values of the three fulfilled promises.
}).catch(function(error) {
    //handle the error. At least one of the promises rejected.
});

당신이 지금 새로운 관점에서 약속을 볼 수 있기를 바랍니다.


124
그들은 실제로 그것을 "약속"으로 지명해서는 안됩니다. "미래"는 100 배 이상 좋습니다.
Pacerier

12
@Pacerier는 Future가 jQuery에 의해 오염되지 않았기 때문에?
Esailija

5
대체 패턴 (원하는 내용에 따라 : api (). then (api2) .then (api3) .then (doWork); 즉, api2 / api3 함수가 마지막 단계에서 입력을 받아 새로운 약속을 반환하면 추가 포장없이 바로 연결할 수 있습니다
Dtipson

1
어떤이 경우은에 operatings 비동기입니다 api2api3? .then비동기 작업이 완료된 후에 만 마지막 으로 호출됩니까?
NiCk Newman

8
왜 나를 태그 했습니까? 나는 문법을 조금 고쳤다. 저는 JS 전문가가 아닙니다. :)
Scott Arciszewski

169

예, 약속은 비동기 콜백입니다. 콜백으로 수행 할 수없는 작업을 수행 할 수 없으며 일반 콜백과 동일한 비동기 문제가 발생합니다.

그러나 약속은 다만 콜백 이상. 그것들은 매우 강력한 추상화이며 오류가 발생하기 쉬운 상용구로 깨끗하고 더 나은 기능 코드를 허용합니다.

그래서 주요 아이디어는 무엇입니까?

약속은 단일 (비동기) 계산 결과를 나타내는 객체입니다. 그들은 그 결과를 한 번만 해결 합니다 . 이것이 의미하는 것이 몇 가지 있습니다.

약속은 관찰자 패턴을 구현합니다.

  • 작업이 완료되기 전에 값을 사용할 콜백을 알 필요가 없습니다.
  • 콜백을 함수의 인수로 기대하지 return않고 Promise 객체를 쉽게 만들 수 있습니다 .
  • 약속은 값을 저장하며 언제든지 원할 때 투명하게 콜백을 추가 할 수 있습니다 . 결과가 나오면 호출됩니다. "투명도"는 약속이 있고 콜백을 추가 할 때 결과가 아직 도착했는지 여부에 상관없이 코드에 영향을 미치지 않음을 의미합니다. API와 계약이 동일하여 캐싱 / 메모 레이션을 크게 단순화합니다.
  • 여러 개의 콜백을 쉽게 추가 할 수 있습니다

약속은 체인 가능하다 ( 모나드 , 당신이 원하는 경우 ) :

  • 약속이 나타내는 값을 변환해야하는 경우 약속에 대해 변환 함수 를 맵핑 하고 변환 된 결과를 나타내는 새 약속을 다시 가져옵니다. 어떻게 든 사용하기 위해 동 기적으로 값을 얻을 수는 없지만 쉽게 들어 올릴 수 있습니다 약속 컨텍스트에서 변환을 . 상용구 콜백이 없습니다.
  • 두 개의 비동기 작업을 연결하려는 경우이 .then()방법을 사용할 수 있습니다 . 첫 번째 결과로 콜백을 호출하고 콜백이 리턴 한 약속의 결과에 대한 약속을 리턴합니다.

복잡한 소리? 코드 예제를위한 시간입니다.

var p1 = api1(); // returning a promise
var p3 = p1.then(function(api1Result) {
    var p2 = api2(); // returning a promise
    return p2; // The result of p2 …
}); // … becomes the result of p3

// So it does not make a difference whether you write
api1().then(function(api1Result) {
    return api2().then(console.log)
})
// or the flattened version
api1().then(function(api1Result) {
    return api2();
}).then(console.log)

평탄화는 마술처럼 나오지 않지만 쉽게 할 수 있습니다. 많이 중첩 된 예제의 경우 (거의) 동등한 것은

api1().then(api2).then(api3).then(/* do-work-callback */);

이러한 메소드의 코드를 이해하면 이해하는 데 도움이 될 것입니다. 여기 몇 줄로 된 가장 기본적인 promise lib가 있습니다 .

약속에 대한 큰 소란은 무엇입니까?

Promise 추상화는 기능의 구성 성을 훨씬 향상시킵니다. 예를 들어, then체인 옆에 있는 all함수는 여러 병렬 대기 약속의 결합 결과에 대한 약속을 만듭니다.

마지막으로 약속에는 통합 오류 처리가 제공됩니다. 계산의 결과는 약속이 가치 로 이행 되거나 이유로 거부 되는 것일 수 있습니다 . 모든 컴포지션 함수는 이것을 자동으로 처리하고 약속 체인에서 오류를 전파하므로 일반 콜백 구현과 달리 어디서나 명시 적으로 신경 쓰지 않아도됩니다. 결국, 발생한 모든 예외에 대해 전용 오류 콜백을 추가 할 수 있습니다.

사물을 약속으로 바꾸어야한다는 것은 말할 것도 없습니다.

실제로 좋은 약속 라이브러리를 사용하는 것은 쉽지 않습니다. 기존 콜백 API를 약속으로 변환하는 방법을 참조하십시오 .


안녕 Bergi, 당신은이 SO 질문에 추가 흥미로운 것이 있습니까? stackoverflow.com/questions/22724883/…
Sebastien Lorber

1
@Sebastien : 나는 스칼라 (아직)에 대해 잘 모른다. 그리고 벤자민이 말한 것을 반복 할 수 있었다 :-)
Bergi

3
.then(console.log)console.log는 콘솔 컨텍스트에 따라 다르므로 사용할 수 없습니다 . 이렇게하면 잘못된 호출 오류가 발생합니다. 사용 console.log.bind(console)또는 x => console.log(x)바인드 컨텍스트.
Tamas Hegedus

3
@hege_hegedus : console메소드가 이미 바인딩 된 환경이 있습니다 . 그리고 물론, 난 단지 모두 중첩을 정확히 같은 동작을했다, 그 중 하나는 - P 일하는 것이하지 않는 것이
BERGI

1
멋 졌어요. 이것이 내가 필요한 것 : 적은 코드와 더 많은 해석. 감사합니다.
Adam Patterson

21

ES6 화살표 기능 약속과 함께 이미 설립 답변뿐만 아니라 겸손하게 빛나는 작은 파란 난쟁이에서 설정 바로 적색 거성으로. 그것은 초신성으로 무너질 것입니다.

api().then(result => api2()).then(result2 => api3()).then(result3 => console.log(result3))

으로 oligofren는 지적 API 호출 사이의 인수없이 모든에서 익명 래퍼 함수가 필요하지 않습니다 :

api().then(api2).then(api3).then(r3 => console.log(r3))

마지막으로, 엄청난 블랙홀 수준에 도달하려면 약속을 기다릴 수 있습니다.

async function callApis() {
    let api1Result = await api();
    let api2Result = await api2(api1Result);
    let api3Result = await api3(api2Result);

    return api3Result;
}

9
"ES6와 기능의 약속이 바로 적색 거성으로 겸손하게 빛나는 작은 블루 스타에서 돌려 화살표 즉, 초신성으로 붕괴에 관한 것입니다."번역 : ES6는 약속과 기능을 화살표 결합하는 최고 :)입니다
user3344977가

3
그것은 약속의 소리를 우주의 재앙처럼 들리게 만드는데, 나는 당신의 의도가 아니라고 생각합니다.
마이클 맥기니스

apiX메소드 에서 인수를 사용하지 않는 경우 화살표 기능을 완전히 건너 뛸 수도 있습니다 api().then(api2).then(api3).then(r3 => console.log(r3)).
oligofren의

@MichaelMcGinnis-약속이 지루한 콜백 지옥에 미치는 영향은 어두운 구석에있는 초신성 폭발과 같습니다.
John Weisz

나는 당신이 그것을 시적으로 의미한다는 것을 알고 있지만 약속은 "초신성"과는 거리가 멀다. 수도원 법을 어기 거나 취소 또는 여러 값 반환과 같은 더 강력한 사용 사례에 대한 지원이 부족합니다.
Dmitri Zaitsev 2016 년

15

위의 멋진 답변 외에도 2 점이 더 추가 될 수 있습니다.

1. 시맨틱 차이 :

약속은 이미 창조되었을 때 해결 될 수 있습니다. 이것은 사건이 아닌 조건을 보증 한다는 것을 의미 합니다 . 이미 해결 된 경우 전달 된 해결 된 함수는 여전히 호출됩니다.

반대로 콜백 은 이벤트를 처리합니다. 따라서 콜백이 등록되기 전에 관심있는 이벤트가 발생한 경우 콜백이 호출되지 않습니다.

2. 제어 역전

콜백 에는 제어 역전이 포함됩니다. API에 콜백 함수를 등록하면 Javascript 런타임은 콜백 함수를 저장하고 실행할 준비가되면 이벤트 루프에서 호출합니다.

설명 은 Javascript 이벤트 루프 를 참조하십시오 .

Promises를 사용하면 제어는 호출 프로그램에 상주합니다. promise 객체를 저장하면 언제든지 .then () 메서드를 호출 할 수 있습니다 .


1
왜 그런지 모르겠지만 이것이 더 나은 대답처럼 보입니다.
radiantshaw

13

다른 답변 외에도 ES2015 구문은 약속과 매끄럽게 혼합되어 더 많은 상용구 코드를 줄입니다.

// Sequentially:
api1()
  .then(r1 => api2(r1))
  .then(r2 => api3(r2))
  .then(r3 => {
      // Done
  });

// Parallel:
Promise.all([
    api1(),
    api2(),
    api3()
]).then(([r1, r2, r3]) => {
    // Done
});

5

약속은 콜백이 아니며 비동기 프로그래밍을 촉진하는 프로그래밍 관용구입니다. 약속을 반환하는 코 루틴이나 생성기를 사용하여 비동기 / 대기 스타일의 프로그래밍을 사용하는 것은 이러한 관용구 중 세 번째로 간주 될 수 있습니다. 다른 프로그래밍 언어 (Javascript 포함)에서이 관용구를 비교 한 내용은 다음과 같습니다. https://github.com/KjellSchubert/promise-future-task


5

아뇨

콜백 은 단순히 다른 함수의 실행이 완료된 후 호출되고 실행되는 JavaScript 의 함수입니다. 어떻게 되나요?

실제로 JavaScript에서 함수 자체는 객체로 간주되므로 다른 모든 객체와 마찬가지로 함수조차도 다른 함수에 대한 인수로 보낼 수 있습니다 . 가장 일반적이고 일반적인 유스 케이스는 JavaScript의 setTimeout () 함수입니다.

약속 은 콜백과 동일한 작업을 수행하는 것과 비교하여 비동기 코드를 처리하고 구조화하는 훨씬 더 즉흥적 인 접근 방식입니다.

Promise는 생성자 함수에서 두 개의 콜백 (resolve 및 reject)을받습니다. 약속 내부의 이러한 콜백은 오류 처리 및 성공 사례에 대한 세밀한 제어를 제공합니다. 확인 콜백은 약속 실행이 성공적으로 수행되었을 때 사용되며 거부 콜백은 오류 사례를 처리하는 데 사용됩니다.


2

콜백에 대한 약속은 아닙니다.

예 js와 함께 javascript native promise를 사용할 수 있습니다

my cloud 9 code link : https://ide.c9.io/adx2803/native-promises-in-node

/**
* Created by dixit-lab on 20/6/16.
*/

var express = require('express');
var request = require('request');   //Simplified HTTP request client.


var app = express();

function promisify(url) {
    return new Promise(function (resolve, reject) {
    request.get(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
        resolve(body);
    }
    else {
        reject(error);
    }
    })
    });
}

//get all the albums of a user who have posted post 100
app.get('/listAlbums', function (req, res) {
//get the post with post id 100
promisify('http://jsonplaceholder.typicode.com/posts/100').then(function (result) {
var obj = JSON.parse(result);
return promisify('http://jsonplaceholder.typicode.com/users/' + obj.userId + '/albums')
})
.catch(function (e) {
    console.log(e);
})
.then(function (result) {
    res.end(result);
}
)

})


var server = app.listen(8081, function () {

var host = server.address().address
var port = server.address().port

console.log("Example app listening at http://%s:%s", host, port)

})


//run webservice on browser : http://localhost:8081/listAlbums

1

JavaScript Promises는 실제로 콜백 함수를 사용하여 Promise가 해결 또는 거부 된 후 수행 할 작업을 결정하므로 근본적으로 다르지 않습니다. Promises의 주요 아이디어는 콜백을 가져 오는 것입니다. 특히 일종의 작업을 수행하려는 중첩 콜백이지만 더 읽기 쉽습니다.


0

약속 개요 :

JS에서는 비동기 작업 (예 : 데이터베이스 호출, AJAX 호출)을 약속으로 래핑 할 수 있습니다. 일반적으로 검색된 데이터에 대해 추가 논리를 실행하려고합니다. JS 약속에는 비동기 작업의 결과를 처리하는 핸들러 함수가 있습니다. 핸들러 함수에는 이전의 비동기 작업의 값에 의존 할 수있는 다른 비동기 작업이있을 수 있습니다.

약속에는 항상 다음 3 가지 상태가 있습니다.

  1. 보류 중 : 모든 약속의 시작 상태이며 이행 또는 거부되지 않았습니다.
  2. 이행 : 작업이 성공적으로 완료되었습니다.
  3. 거부 됨 : 작업이 실패했습니다.

보류중인 약속은 값으로 해결 / 완료 또는 거부 될 수 있습니다. 그런 다음 콜백을 인수로 취하는 다음 핸들러 메소드가 호출됩니다.

  1. Promise.prototype.then() : 약속이 해결되면이 함수의 콜백 인수가 호출됩니다.
  2. Promise.prototype.catch() : 약속이 거부되면이 함수의 콜백 인수가 호출됩니다.

위의 기술은 콜백 인수를 얻지 만 콜백 만 사용하는 것보다 훨씬 우수합니다. 여기에서 많은 것을 명확히하는 예제가 있습니다.

function createProm(resolveVal, rejectVal) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (Math.random() > 0.5) {
                console.log("Resolved");
                resolve(resolveVal);
            } else {
                console.log("Rejected");
                reject(rejectVal);
            }
        }, 1000);
    });
}

createProm(1, 2)
    .then((resVal) => {
        console.log(resVal);
        return resVal + 1;
    })
    .then((resVal) => {
        console.log(resVal);
        return resVal + 2;
    })
    .catch((rejectVal) => {
        console.log(rejectVal);
        return rejectVal + 1;
    })
    .then((resVal) => {
        console.log(resVal);
    })
    .finally(() => {
        console.log("Promise done");
    });

  • createProm 함수는 1 초 후 임의의 Nr을 기반으로 해결되거나 거부되는 약속을 작성합니다.
  • 약속이 해결되면 첫 번째 then메소드가 호출되고 해결 된 값이 콜백의 인수로 전달됩니다.
  • 약속이 거부되면 첫 번째 catch메소드가 호출되고 거부 된 값이 인수로 전달됩니다.
  • catchthen방법은 우리가 그들을 체인 이유입니다 그 약속을 반환합니다. 반환 된 값 Promise.resolve과 ( throw키워드를 사용하여 ) 던진 값을에 래핑합니다 Promise.reject. 따라서 반환 된 모든 값은 약속으로 변환 되며이 약속에서 다시 핸들러 함수를 호출 할 수 있습니다.
  • Promise 체인은 중첩 콜백보다 더 세밀하게 조정 된 제어 및 더 나은 개요를 제공합니다. 예를 들어, catch메소드는 catch핸들러 이전에 발생한 모든 오류를 처리 합니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.