Javascript의 Promise

Author Avatar
Jong-Ho Jeong Sep 06, 2016

javascript의 Promise란?

async, 비동기는 웹개발을 공부하다보면 필수적으로 접하게 되는 용어 중 하나이다. 비동기란 말 그대로 동기적이지 않다는 뜻이다. 요청한 작업이 언제 끝날지 모르고, 그동안 쓰레드는 다른일도 해야한다. 그 자리에서 결과가 나오지 않기 때문에 결과가 나왔음을 어떻게 알려줄것이지에 대한 해결책이 필요하다.

이에 대한 해결책이 함수를 호출하는 것이다. 언젠가 등록해 놓은 함수를 호출하기로 약속하고, 어떤 작업이 끝나면 약속한 함수를 호출 하는 것을 통해서 이 작업이 끝났다는 것을 알려준다. 이런 방법을 비동기적이라고 하고 여기서 호출하는 함수를 콜백함수라고 한다.

그런데 이런 비동기적인 방법으로 어떤 작업이 끝나서 콜백함수가 호출되었는데 여기서 얻은 값을 통해 또 다른 비동기함수를 호출한다면, 또한 이런 일이 몇겹으로 일어난다면 코드의 가독성이 매우 떨어지게 된다.

myFunction1(...args, function(){
    myFunction2(...args, function(){
        myFunction3(...args, function(){
            ...
            // 가독성 저하
            ...
        })
    })
})


이런 경우를 흔히 call back hell, 콜백지옥이라고 일컫는다. ES6에서는 지옥에서 살아남는데 도움이 되기 위해서 Promise라는 키워드를 도입한다.

Promise는 언젠가 완료될 것으로 기대되는 객체의 표현이다.

누군가와 약속을 하면 결과적으로 세가지 상태가 있을 수 있다.

  • 약속을 했는데 아직 결과는 모름
  • 약속이 지켜짐
  • 약속이 지켜지지않음

마찬가지로 Promise객체는 세가지 상태를 가질 수 있다.

  • 대기중 (pending)
  • 이행됨 (fulfilled)
  • 거부됨 (rejected)

Promise 생성

Promise객체는 익명함수를 인자로 받아서 생성된다. 약속이 잘 지켜졌다면 resolve를, 그렇지 않다면 reject를 호출 한다.

myPromise = function(isValid){
    return new Promise(function(resolve, reject){
        if(isValid){
            resolve('success');
        } else{
            reject(Error('fail'))
        }
    })
}

Promise 객체를 생성하면 우리가 한 약속은 대기중(pending)상태이고 후에 어떤 요인에 의해 이행되거나(fulfilled) 거부될것이다.(rejected)

아래처럼 new 키워드를 통해서도 생성할 수 있다.

var myPromise = new Promise(function(resolve, reject){
    // 랜덤하게 성공
    var isValid = Math.round(Math.random());

    if(isValid){
        resolve('success');
    } else{
        reject(Error('fail'));
    }
})

이 경우엔 Promise객체를 생성하자마자 인자로 받은 익명함수를 실행할것이다. 또한 한번 할당된 myPromise는 유지되기 때문에 처음에 성공했다면 후의 호출에서도 계속 성공, 처음에 실패했다면 후의 호출에서도 계속 실패할 것이다.

Promise의 이행

앞의 예제처럼 Promise객체는 resolve되거나 reject 될 것이다. 이때 then과 catch를 사용할 수 있다.

myPromise(true | false).then(funcToResolve, funcToReject);
myPromise(true | false).then(funcToResolve).catch(funcToReject);
myPromise(true | false).then(funcToResolve).then(undefined, funcToReject);

위의 세가지 예제에서는 모두 myPromise가 이행된다면 funcToResolve가 호출 될것이고 거부되었다면 funcToReject가 호출될것이다. 그런데 첫번째 예제와 두번째 예제에는 한가지 차이가 있는데 첫번째 예제의 경우 funcToResolve에서 발생하는 에러를 핸들링할 수 없다는점이다.

두번째 예제의 경우 funcToResolve에서 에러가 발생할 경우 catch를 통해서 잡을 수 있기 때문에 두번째방법을 주로 이용하는 것이 좋다.

맨 아래의 예제에서 알 수 있듯이 then은 인자로 받은 함수를 통해 결정되는 Promise객체를 다시 반환한다. 따라서 계속해서 chaining이 가능하다.

이때 주의할 점은 undefined를 반환하지 말아야 한다는 것이다. 가장 많이 하는 실수는 아무것도 반환하지 않는것이다. 이런 경우 문제가 발생할 수 있다. 또한 then의 인자로 함수가아닌 타입의 값을 전달하게 되면 then(null)로 처리되기 때문에 반드시 함수타입을 전달해야 한다.

Promise.all

Promise.all은 Promise객체의 배열을 파라미터로 받아서 하나라도 거부된 경우 거부를 반환한다.

var myPromise1 = new Promise(function(resolve, reject){
    window.setTimeout(function(){
        resolve('myPromise1 result');
    }, 1000)
})

var myPromise2 = new Promise(function(resolve, reject){
    window.setTimeout(function(){
        resolve('myPromise2 result');
    }, 3000)
})

Promise.all([myPromise1, myPromise2]).then(function(result){
    console.log(result);
})

// 3000ms 후에 ["myPromise1 result", "myPromise2 result"] 가 출력된다.


아래 링크에서 더 자세한 내용을 볼수 있다. https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise http://www.html5rocks.com/ko/tutorials/es6/promises/ http://yubylab.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Promise-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0