프로미스는 비동기 작업을 조금 더 편하게 처리할 수 있도록 ES6에 도입된 기능이다. 이전에는 비동기 작업을 처리할 때에는 콜백 함수로 처리를 해야 했었는데, 콜백 함수로 처리를 하게 된다면 비동기 작업이 많아질 경우 코드가 쉽게 난잡해질 수 있다.
한번 숫자 n을 파라미터로 받아와서 다섯번에 걸쳐 1초마다 1씩 더해서 출력하는 작업을 setTimeout으로 구현하면 무수히 많은 콜백의 연속을 볼 수 있다.(콜백지옥)
function increaseAndPrint(n, callback) {
setTimeout(() => {
const increased = n + 1;
console.log(increased);
if (callback) {
callback(increased);
}
}, 1000);
}
increaseAndPrint(0, n => {
increaseAndPrint(n, n => {
increaseAndPrint(n, n => {
increaseAndPrint(n, n => {
increaseAndPrint(n, n => {
console.log('끝!');
});
});
});
});
});
이런 식으로 읽기 어려운 코드를 Callback Hell(콜백지옥)이라고 부른다.
비동기적으로 처리해야 하는 일이 많아질수록, 코드의 깊이가 깊어지는 현상을 방지할 수 있다.
const MyPromise = new Promise((resolve, reject) => {
// 구현 ...
})
Promise는 성공할 수도 있고, 실패할 수도 있다. 성공할 때에는 resolve를 호출해주면되고, 실패할 때는 reject를 호출해주면 된다.
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 1000);
});
myPromise.then(n => {
console.log(n);
});
resolve를 호출할 때 특정 값을 파라미터로 넣어주면, 이 값을 작업이 끝나고 나서 사용할 수 있다. 작업이 끝나고 다시 또 다른 작업을 해야할 때에는 Promise 뒤에.then(...)
을 붙여서 사용하면 된다.
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error());
});
myPromise
.then(n => {
console.log(n);
})
.catch(error => {
console.log(error);
});
실패하는 상황에서는 reject
를 사용하고, .catch
를 통해 실패했을 시 수행할 작업을 설정할 수 있다.
function increaseAndPrint(n) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const value = n + 1;
if (value === 5) {
const error = new Error();
error.name = 'ValueIsFiveError';
reject(error);
return;
}
console.log(value);
resolve(value);
}, 1000);
});
}
increaseAndPrint(0).then((n) => {
console.log('result: ', n);
})
Promise의 속성 중에는, 만약 then 내부에 넣은 함수에서 또 Promise를 리턴하게 된다면, 연달아서 사용할 수 있다.
function increaseAndPrint(n) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const value = n + 1;
if (value === 5) {
const error = new Error();
error.name = 'ValueIsFiveError';
reject(error);
return;
}
console.log(value);
resolve(value);
}, 1000);
});
}
increaseAndPrint(0)
.then(n => {
return increaseAndPrint(n);
})
.then(n => {
return increaseAndPrint(n);
})
.then(n => {
return increaseAndPrint(n);
})
.then(n => {
return increaseAndPrint(n);
})
.then(n => {
return increaseAndPrint(n);
})
.catch(e => P
console.error(e);
});
위 코드는 다음과같이 정리할 수 있다.
function increaseAndPrint(n) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const value = n + 1;
if (value === 5) {
const error = new Error();
reject(error);
return;
}
console.log(value);
resolve(value);
}, 1000);
});
}
increaseAndPrint(0)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.catch(e => {
console.error(e);
});
Promise를 사용하면, 비동기 작업의 갯수가 많아져도 코드의 깊이가 깊어지지 않게 된다.
하지만 Promise도 불편한 점이 있긴 마찬가지이다. 에러를 잡을 때 몇번째에서 발생했는지 알아내기도 어렵고 특정 조건에 따라 분기를 나누는 작업도 어렵고, 특정 값을 공유해가면서 작업을 처리하기도 까다롭다. async/await를 사용하면, 이러한 문제점을 해결할 수 있다.