function add5(num, callback) {
console.log(num); // 0, 5, 10, 15
setTimeout(() => callback(num + 5), 1000);
}
add5(0, function(a) {
add5(a, function(b) {
add5(b, function(c) {
add5(c, function(d) {
console.log(d); // 20
})
})
})
});
위에서 말했다 싶이 비동기 함수는 런타임시 모든 코드가 실행이 되고 난 다음에 실행이 된다.
비동기 함수가 실행되고 난 “다음에” 비동기 처리의 결과물을 바탕으로 코드를 실행하고 싶을때, 아주 예전에는 위와 같이 콜백 방식으로 처리했다.
콜백 함수란 위와 같이 함수(add5)의 인자에 함수(callback)를 넘겨, 해당 함수(add5) 안에서 호출되는 함수(callback)이다.
add5 함수안에서 callback 함수가 실행되니, add5 함수 안의 코드가 실행되고 난 뒤 callback함수가 실행된다는 “순서가 보장이 된다.”
위 코드를 실행한 결과이다. setTimeout 함수에서 설정한 1초 만큼의 시간 뒤에 차례대로 5씩 더하는 결과를 볼 수 있다. 비동기 처리가 순서대로 잘 보장되어 실행되었다.
하지만 콜백함수의 코드를 보면 바로 문제점을 알 수 있는데, 뎁스가 점점 깊어져 가독성이 매우 안좋다. 이걸 보고 콜백 헬(callback hell)이라고 부른다.
function add5(num) {
console.log(num); // 0, 5, 10, 15
return new Promise(resolve => {
setTimeout(() => resolve(num + 5), 1000);
});
}
add5(0)
.then((a) => add5(a))
.then((b) => add5(b))
.then((c) => add5(c))
.then((d) => {
console.log(d); // 20
});
위와 같은 문제 때문에 ES6에서 Promise 문법이 나왔다.
Promise 는 생성자 함수이다. 생성자 함수 Promise 를 호출하면 Promise 인스턴스 객체를 리턴한다.
Promise 객체는 then이라는 메서드가 있다. 이 then 메서드를 호출할 때 인수로 콜백함수를 넣으면 Promise 에서 resolve로 받아서 비동기 처리를 수행한다. 위 콜백 예시의 callback 인수와 Promise 예시의 resolve 인수가 비슷하게 동작한다.
resolve : (a) => add5(a)
num + 5 : a
위 처럼 대응된다고 생각하면 좀 더 코드를 쉽게 읽을 수 있을 것 같다.
then을 계속 체이닝해서 사용할 수 있는데 그 이유는 then 메서드는 또 다시 Promise 객체를 리턴하기 때문에 then을 연속해서 사용할 수 있는 것이다.
이때 전자의 then 메서드의 인수로 들어간 콜백함수((a) => add5(a))의 return 값(add5(a))이 다음 then 메서드의 콜백함수((b) => add5(b))의 인자(b)로 들어온다. 즉 add5(a) 값이 b가 된다.