콜백 함수 시리즈


2019. 06. 03 수정

 

1. Promise란?

MDN에 따르면 위와 같이 JS Promise를 설명하고 있습니다.

Promise 단어 그대로 나중에 연산하기 위한 "약속"과 같은 개념이죠.

 

Promise는 비동기 요청에 대하여, 비동기 실행이 완료된 후 결과 값 또는 실패의 이유를 콜백 함수로 전달합니다.

따라서 비동기 실행이 정상적으로 되었는지, 오류가 발생했는지 알 수 있도록 상태가 정의되어 있어야 합니다.

 

상태

  • fulfilled
    • 비동기 동작 정상 완료
  • rejected
    • 비동기 동작 중 에러 발생

 

다음은 promise를 사용하는 간단한 예제이며, 각 상태가 어떻게 연결되는지 보여주고 있습니다.

var test = function(bool){
    return new Promise(function(resolve, reject){
        setTimeout( function(){
            if(bool){
                resolve("fulfilled 상태입니다. then으로 연결됩니다.");
            }
            else{
                reject("rejected 상태입니다. catch로 연결됩니다.");
            }
        }, 1000)
    })
}

test(true)
.then( function(result){
    console.log(result);
})
.catch( function(err){
    console.log(err)
})

먼저 Promise 객체를 생성하는 부분입니다.

Promise 생성자 함수에 콜백함수를 정의하는데요, 매개변수로 resolve, reject를 정의합니다.

처리하는 로직상에서 비동기 처리가 정상적으로 수행되면 resolve() 함수를 호출하고, 비동기 처리가 실패하면 reject() 함수를 호출합니다.

 

 

Promise 생성자의 prototype에는 then(), catch() 메서드가 존재합니다.

  • then() 메서드는 호출한 Promise가 resolve()를 호출했을 때 반환값을 콜백함수로 받아주며, 이 때 새로운 Promise를 반환합니다.
  • catch() 메서드는 호출한 Promise가 reject()를 호출했을 때 반환값을 콜백함수로 받아주며, 이 때 새로운 Promise를 반환합니다.

 

위의 예제는 test() 함수를 호출할 때 true를 인자로 전달했으므로 비동기 함수인 setTimeout()에서 resolve() 메서드가 실행되기 때문에

"fulfilled 상태입니다. then으로 연결됩니다." 문구가 출력될 것입니다.

test() 함수를 호출할 때 false를 인자로 전달해보시면, catch()가 실행될 것입니다.

즉, then()과 catch()는 동시에 수행되지 않습니다.

 

 

 

 

2. then(), catch()을 활용한 비동기 동작 연결

위에서 보신 것 처럼 Promise 생성자의 then() 메서드와 catch() 메서드는 새로운 Promise를 반환하므로 비동기 동작을 연결 시킬 수 있습니다.

var test = function(bool){
    return new Promise(function(resolve, reject){
        setTimeout( function(){
            if(bool){
                resolve("fulfilled 상태입니다. then으로 연결됩니다.");
            }
            else{
                reject("rejected 상태입니다. catch로 연결됩니다.");
            }
        }, 1000)
    })
}

test(true)
.then( function(result){
    console.log("1) " + result);
    return test(true);
})
.then( function(result){
    console.log("2) " + result);
    return test(false);
})
.then( function(result){
    console.log("3) " + result);
    return test(true)
})
.catch( function(result){
    console.log("4) " + result);
    return test(true)
})
.then( function(result){
    console.log("5) " + result);
    return test(true)
})

 

then(), catch() 메서드를 통해 비동기 동작을 연결시키는 예제입니다.

catch() 메서드를 호출했다고 해서 끝나는 것이 아니라 새로운 Promise()를 반환한다는 것에 유의해야 합니다.

 

3) 부분은 호출되지 않았는데요,

그 이유는 2)번에서 false를 인자로 넘겨 test() 함수를 호출했기 때문에 reject() 함수가 실행되어 catch()가 실행됐기 때문입니다.

 

 

 

 

3. Promise의 메서드 종류

지금까지 Promise의 메서드 중 resolve()reject()에 대해서만 알아보았는데요.

MDN에서는 Promise 객체의 메서드를 다음과 같이 소개하고 있습니다.

 

 

 

다음으로 각각의 예제를 살펴보도록 하겠습니다.

1) all

  • all() 메서드를 호출할 때, 인자로 여러 Promise들을 넘겨줍니다.
  • 모든 promise가 fulfiled 되거나, promise가 하나라도 rejected된 경우 promise를 반환합니다.
    • 모든 promise가 fulfiled 되면 배열로써 결과를 전달하고,
    • promise가 하나라도 rejected된 경우 reject된 이유를 반환합니다.
var promise1 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 2000, "promise1");
});
var promise2 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 1000, "promise2");
});

Promise.all([promise1, promise2]).then(function(value) {
    console.log(value);
});

 

promise1과 promise2 모두 resolve() 메서드를 호출했으므로, 모든 promise가 fulfiled 됐으니 결과는 배열이 됩니다.

 

 

 

2) race

  • race() 메서드를 호출할 때, 인자로 여러 Promise들을 넘겨줍니다.
  • fulfiled , rejected 상관없이 처음으로 종료된 promise를 반환합니다.
var promise1 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 2000, "promise1");
});
var promise2 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 1000, "promise2");
});

Promise.race([promise1, promise2]).then(function(value) {
    console.log(value);
});

위 예제에서 promise1은 setTimeout이 2초이고, promise2는 1초입니다.

즉, 비동기 실행인 setTimeout이 promise2가 더 빨리 종료되므로 결과는 promise2가 출력됩니다.

 

 

reject()resolve() 두 메서드는 위에서 살펴봤으니 생략하도록 하겠습니다.

 

 

 

 

4. 올바르지 못한 promise 사용

promise는 비동기 처리에서 콜백 지옥을 해결하기 위해 고안되었습니다.

그런데 promise를 사용해 또 다시 콜백을 만드는 코드를 짤 수 있는데요, 이는 promise를 잘 사용하고 있지 않은 것입니다.

 

foo.someFunction().then(function(result){
    // ... 어떤 로직이 수행 된다.
    goo.anotherFunction().then(function(result2){
        // ... 어떤 로직이 수행된다.
    }).catch( function(err){
        console.log(err);
    })
})

위의 예제는 promise를 제대로 사용하지 못한 예입니다.

promise를 사용하여 then()catch() 메서드를 사용하고 있지만, 콜백지옥은 여전합니다.

 

foo.someFunction().then(function(result){
    // ... 어떤 로직이 수행 된다.
    return goo.anotherFunction()
}).then(function(result2){
    // ... 어떤 로직이 수행된다.
}).catch( function(err){
    console.log(err);
})

수정된 코드는 promise를 사용하면 비동기 동작을 단순화 시킬 수 있다는 것을 염두한 코드입니다.

훨씬 깔끔해진 것을 확인할 수 있습니다.

 

 

 

이상으로 promise에 대해 알아보았습니다.

Promise보다 async/await을 사용하면 조금 더 간결하게 비동기 코드를 작성할 수 있는데요.

async/await와 관련된 글은 여기를 참고해주세요.

 

[ 참고 ] 

  • https://yubylab.tistory.com/entry/자바스크립트-Promise-이해하기
  • http://beomy.tistory.com/11