비동기 작업의 최종 완료 또는 실패를 나타내는 객체, ES6에 추가된 문법이다.
기존의 Callback 방식은 이런 문제점이 있었다.
1. Callback 지옥
2. callback에 값을 반환해서 사용하기 어려움
3. 각 callback 마다 에러 핸들링을 독립적으로 해야함
function callbackHell() {
setTimeout(() => {
let res = 1;
console.log(res);
setTimeout(() => {
console.log(++res);
setTimeout(() => {
console.log(++res);
}, 1000);
}, 1000);
}, 1000);
}
코드와 함께 callback의 결함을 이해하고 Promise에서 어떻게 개선됐는지 보자.
위의 코드는 연속적인 비동기 처리를 한 것이다.
하위 callback들만 상위 callback의 결과값을 사용하고 있는 모습을 볼 수 있다. 콜백지옥현상을 마주할 수 있다.
이제 Promise를 사용해서 변경된 코드를 보자.
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 1000);
});
const promise2 = promise1.then((res) =>
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(++res);
}, 1000));
콜백지옥에서 벗어났다. 비동기 처리에 대한 결과값을 반환받아 사용할 수 있다.
callback 내부에서 에러가 발생할 수 있으며 각 callback마다 독립적으로 try catch를 작성해야한다.
(비동기로 작성된 코드의 에러는 try catch가 잡아낼 수 없어서, 비동기 callback 내부에 일일이 try catch를 작성해서 에러를 잡아줘야한다.)
function callbackHell() {
setTimeout(() => {
try {
let res = 1;
console.log(res);
setTimeout(() => {
try {
console.log(++res);
setTimeout(() => {
try {
console.log(++res);
}
catch (error) {
console.log('3번 콜백 에러 발생');
}
}, 1000);
} catch (error) {
console.log('2번 콜백 에러 발생');
}
}, 1000);
} catch (error) {
console.log('1번 콜백 에러 발생');
}
}, 1000);
}
동기적인 코드에서의 에러 처리의 아이디어를 빌려, Promise가 비동기 프로그래밍에서의 에러처리를 동기 프로그래밍에서의 에러처리와 유사하게 개선했다.
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("1번 콜백 에러");
}, 1000);
})
.then((res) =>
new Promise((resolve, reject) => {
setTimeout(() => {
reject("2번 콜백 에러");
}, 1000);
})
)
.catch((error) => {
console.log(error);
});
각 callback 내부에서 에러처리를 해줄 필요 없이, catch함수만 한 번 호출해주면 이전 단계에서 발생한 에러들은 catch가 잡아내서 처리한다.
기존 연속적인 비동기 작업의 에러들을 처리할 때 독립적으로 에러를 핸들링 했었던 게 개선되었다.
동기식 프로그래밍에서 try catch코드를 작성할 때, try에서 에러 발생 시 catch에서 잡아내는 방식과 유사한 것 같다.
Promise가 등장함으로서, 비동기 프로그래밍에 있어서 코드를 이해하고, 작성하는데 드는 비용을 줄여 비동기 프로그래밍에 대한 부담이 덜어진 것 같다.
이후(Ecmascript 2017)에는 더 동기 프로그래밍과 유사하게 코드를 작성할 수 있는 문법으로 async, await이 등장했다.