Skip to content

[ES6] Promises(1): the API

KwanUngPark edited this page Oct 7, 2016 · 22 revisions

이 문서는 http://www.2ality.com/2014/10/es6-promises-api.html 를 번역한 내용입니다.

#목차

  1. Promises
  2. 첫번째 예제
  3. Promises 생성과 사용
  4. promise 생산(Producing a promise)

이번 포스트는 일반적인 promise를 통한 비동기 프로그래밍과 ES6 promise API에 일부를 소개하고자 합니다. 2개의 비동기 프로그래밍 포스트중 2번째이며, 충분히 이해하기 위해선 1번째 포스트를 읽어보는것이 좋을 것입니다.

#1. Promises Promises는 비동기 프로그래밍의 한부분을 도와주는 패턴입니다.(:함수 또는 메서드를 비동기적으로 결과를 받는 것) 이런 기능을 구현하기 위해선 당신은 결과를 위해 지정해 놓은 객체인(an object that is a placeholder for the result) promise 를 반환해야합니다.
함수 호출자는 연산된 결과를 알려주는 promise와 함께 콜백을 등록합니다.(The caller of the function registers callbacks with the promise to be notified once the result has been computed) 이 함수는 promise를 통해 결과를 전송합니다.

자바스크립트 promises의 사실상 표준은 Promises/A+ 이라고 부릅니다. ECMAScript6 promise API는 표준을 따릅니다.

#2. 첫번째 예제 첫번째 예제를 봅시다, to give you a taste of what working with promises is like.
Node-js스타일 콜백으로 비동기적으로 파일 읽는 법은 다음과 같습니다.

    fs.readFile('config.json',
        function (error, text) {
            if (error) {
                console.error('Error while reading config file');
            } else {
                try {
                    var obj = JSON.parse(text);
                    console.log(JSON.stringify(obj, null, 4));
                } catch (e) {
                    console.error('Invalid JSON in file');
                }
            }
        });

같은 기능을 promise 로 구현하면 다음과 같습니다.

    readFilePromisified('config.json')
    .then(function (text) { // (A)
        var obj = JSON.parse(text);
        console.log(JSON.stringify(obj, null, 4));
    })
    .catch(function (reason) { // (B)
        // File read error or JSON SyntaxError
        console.error('An error occurred', reason);
    });

promise도 여전히 콜백이 존재합니다. 그러나 promise는 결과를 호출 할 수 있는 메서드를 통해 제공됩니다.(then(),catch()) (B)라인안에 error 콜백은 2가지의 편리성을 가지고 있습니다. 첫 번째, 에러 처리를 한가지 방식으로 할 수 있습니다. 두 번째로, 당신은 readFilePromisified()와 (A)라인의 콜백 둘다 에러를 핸들링 할 수 있습니다.

#3.Promises 생성과 사용

promise가 어떻게 작동하는지 공급자(Producer)와 소비자(consumer) 측면에서 알아 봅시다.

3-1. promise 생산(Producing a promise)

공급자로써, promise를 생성하고 결과를 전송합니다.

    var promise = new Promise(
        function (resolve, reject) { // (A)
            ...
            if (...) {
                resolve(value); // success
            } else {
                reject(reason); // failure
            }
        });

promise는 항상 아래 3가지 상태중(상호배타적인) 1가지 상태 입니다.

  • 대기(pending) : 아직 결과 처리가 안됬다.
  • 성공(Fulfilled) : 성공적으로 완료되었다.
  • 거절(rejected) : 처리되는 동안 실패가 발생하였다.

promise는 성공(fulfilled)이나 거절(rejected)중 하나로 고정 됩니다.(연산이 끝났을 때) (A promise is settled (the computation it represents has finished) if it is either fulfilled or rejected.) promise 상태는 한번만 지정되며, 그 후 상태를 유지합니다. 그 후에 상태를 바꾸려고 해도 아무 변화가 없습니다.

promise state

new Promise()의 파라미터는( (A)라인 시작점 ) 집행자(executor 모호한 단어라 이하 영문표기로 명칭)라고 부릅니다.

  • 만약 연산이 잘 되었다면, executorresolve() 통해 결과를 전송 합니다. 보통 promise 성공(fulfills)을 말합니다.(promise가 resolve였지만 실제로 아닐 경우 뒤에 설명하겠습니다.)
  • 만약 에러가 발생할 경우, executorreject()를 통해 promise-소비자(consumer)에게 통보 합니다. 즉 promise는 거절(reject) 상태입니다.

##3.2 promise의 사용(Consuming a promise) promise 소비자(consumer)로써, 당신은 then() 메서드에 등록한 콜백을 통해 성공 또는 거절 상태 알림을 받게 됩니다.

    promise.then(
        function (value) { /* fulfillment */ },
        function (reason) { /* rejection */ }
    );

promise 상태가 설정되면 더이상 어느것도 변하지 않게 됩니다. 그래서 promise는 비동기함수를 위하여(일회성 결과) 유용하게 만들어졌습니다. 게다가 promise는 경쟁상태(race condition)를 일으키지 않습니다. 왜냐면 then()promise 전이나 상태가 세팅된 후에 실행하든 중요하지 않기 때문입니다.

  • 전자의 경우, promise 상태가 세팅된 직후 호출 됩니다.
  • 후자의 경우, promise 결과(성공 또는 거절의 결과값)는 캐시돼고, 적절한 반응을 즉시 다룰 수 있게 됩니다.(task 큐와 같이), the promise result (fulfillment value or rejection value) is cached and handed to the appropriate reaction “immediately” (queued as a task).

##3.3 성공 또는 거절만 처리(Only handling fulfillments or rejections) 만약 당신이 성공에만 관심 있다면, then()의 2번째 파라미터를 생략 할 수 있습니다.

    promise.then(
        function (value) { /* fulfillment */ }
    );

만약 당신이 거절에만 관심있다면, 1번째 파라미터를 생략 할 수 있습니다. catch()메서드는 동일한 작동을 하게 해주는 더 적합한 방법입니다.

    promise.then(
        null,
        function (reason) { /* rejection */ }
    );
    
    // Equivalent:
    promise.catch(
        function (reason) { /* rejection */ }
    );

catch()메서드를 사용하는 것은 성공 상태를 배제하고 오류만 잡기 위하여 then()을 사용할 때 추천 하는 방식 입니다. 왜냐하면 catch는 나이스한 콜백 식별자이며, 또 동시간대에 여러개 promise의 거절상태를 처리 할 수 있습니다. (어떻게 하는지 나중에 설명)

#4. 예제(Examples)

몇가지 예제를 통하여 기본적인 빌딩 블록(코드블록's)을 사용해봅시다.

##4.1 예제:promisifying XMLHttpRequest 이벤트 기반인 XMLHttpRequest API 통해 HTTP GET 메서드를 수행하는 promise기반 함수 입니다.

    function httpGet(url) {
        return new Promise(
            function (resolve, reject) {
                var request = new XMLHttpRequest();
                request.onreadystatechange = function () {
                    if (this.status === 200) {
                        // Success
                        resolve(this.response);
                    } else {
                        // Something went wrong (404 etc.)
                        reject(new Error(this.statusText));
                    }
                }
                request.onerror = function () {
                    reject(new Error(
                        'XMLHttpRequest Error: '+this.statusText));
                };
                request.open('GET', url);
                request.send();    
            });
    }

httpGet() 사용방법

    httpGet('http://example.com/file.txt')
    .then(
        function (value) {
            console.log('Contents: ' + value);
        },
        function (reason) {
            console.error('Something went wrong', reason);
        });

##4.2 예제: delaying an activity promise 기반으로 setTimeout()을 구현한 delay()함수(`Q.delay()랑 비슷)

    function delay(ms) {
        return new Promise(function (resolve, reject) {
            setTimeout(resolve, ms); // (A)
        });
    }
    
    // Using delay():
    delay(5000).then(function () { // (B)
        console.log('5 seconds have passed!')
    });

(A)라인에서 파라미터없이 resolve를 호출합니다.(resolve(undefined)를 호출하는 것과 동일). (B)라인에 성공 결과 값은 필요없습니다. 그냥 통보만할 뿐 충분 합니다.

##4.3 예제: timing out a promise(promise의 시간 초과)

    function timeout(ms, promise) {
        return new Promise(function (resolve, reject) {
            promise.then(resolve);
            setTimeout(function () {
                reject(new Error('Timeout after '+ms+' ms')); // (A)
            }, ms);
        });
    }

시간경과 이후 거절((A)라인) 요청을 취소하진 않으면, 성공 결과가 수행(return)되지 않도록 방지 할 뿐 입니다.

timeout()메서드를 사용하면 다음과 같습니다.

    timeout(5000, httpGet('http://example.com/file.txt'))
    .then(function (value) {
        console.log('Contents: ' + value);
    })
    .catch(function (reason) {
        console.error('Error or timeout', reason);
    });

#5. then() 체이닝 메소드 호출 결과는 새로운 promise Q입니다.

    P.then(onFulfilled, onRejected)

이 의미는 Qthen()을 호출을 통해서 promise기반 흐름을 제어 할 수 있게 유지 한다는 것 입니다.

  • QonFulfilled 또는 onRejected 중 하나에 의해 반환된 것으로 resolved 합니다.
  • QonFulfilled 또는 예외를 던진 onRejected중 하나에 의해 rejected 합니다.

##5.1 일반 값으로 해결(Resolving with normal values) 만약 당신이 일반값으로 then()에 의해 반환 되는 promise Q를 해결(resolve) 하면, 그 다음 then()을 통해 일반 값을 받을 수 있습니다.

    asyncFunc()
    .then(function (value1) {
        return 123;
    })
    .then(function (value2) {
        console.log(value2); // 123
    });

##5.2 Resolving with thenable(thenable로 해결) 당신은 또한 thenable R을 가진 then()을 반환 하는 promise Q를 해결(resolve) 할수 있습니다. A thenablepromise 스타일 then()메서드를 가진 객체 입니다. 그래서 promisesthenable 입니다.

위키

기술 문서
Clone this wiki locally