이 글은 『자바스크립트 딥 다이브』를 공부하며 정리한 내용입니다.
이전 게시물에서 자바스크립트의 전통적인 비동기 처리 방식인 콜백 패턴에는 다음과 같은 한계가 있다고 했다.
이러한 문제를 해결하기 위해 ES6에서 프로미스(Promise) 가 도입되었다.
Promise 생성자 함수를 new 연산자와 함께 호출하면 프로미스 객체가 생성된다.
생성자 함수는 콜백 함수를 인수로 받으며, 이 콜백은 resolve와 reject 함수를 전달받는다.
const promise = new Promise((resolve, reject) => {
if (/* 비동기 처리 성공 */) {
resolve("result");
} else {
reject("failure reason");
}
});
프로미스는 내부에서 비동기 작업을 수행하고,
성공 시 resolve, 실패 시 reject 함수를 호출한다.
| 상태 | 의미 | 상태 변경 조건 |
|---|---|---|
| pending | 비동기 처리가 아직 수행되지 않은 상태 | 프로미스가 생성된 직후 |
| fulfilled | 비동기 처리가 성공적으로 완료된 상태 | resolve() 호출 |
| rejected | 비동기 처리가 실패한 상태 | reject() 호출 |
// 성공
const fulfilled = new Promise(resolve => resolve(1));
// 실패
const rejected = new Promise((_, reject) => reject(new Error("에러 발생!")));


비동기 처리가 성공하면 프로미스는 fulfilled 상태가 되고, 결과값을 저장한다.
실패하면 rejected 상태가 되어 Error 객체를 값으로 가진다.
즉, 프로미스는 비동기 처리의 상태와 결과를 관리하는 객체다.
비동기 작업이 완료되면 후속 처리를 수행해야 한다.
이를 위해 프로미스는 다음 3가지 후속 처리 메서드를 제공한다.
| 메서드 | 설명 |
|---|---|
then | 성공(fulfilled) 또는 실패(rejected) 시점의 콜백 등록 |
catch | 실패(rejected) 시점의 콜백 등록 |
finally | 성공/실패와 관계없이 항상 실행되는 콜백 등록 |
then 메서드는 두 개의 콜백 함수를 인수로 받는다.
new Promise(resolve => resolve("fulfilled"))
.then(v => console.log(v), e => console.error(e));
// → "fulfilled"
new Promise((_, reject) => reject(new Error("rejected")))
.then(v => console.log(v), e => console.error(e));
// → Error: rejected
then은 항상 새로운 프로미스를 반환한다.
내부에서 반환한 값이 프로미스가 아니면, 그 값을 resolve로 감싸 새 프로미스를 만든다.
catch는 하나의 콜백만 인수로 받으며,
rejected 상태일 때만 호출된다.
new Promise((_, reject) => reject(new Error("rejected")))
.catch(e => console.error(e));
// → Error: rejected
finally는 성공/실패와 관계없이 항상 한 번 실행된다.
new Promise(() => {})
.finally(() => console.log("finally"));
// → finally
finally는 공통적으로 실행해야 하는 작업(로딩 해제, 연결 종료 등)에 유용하다.
이전 글의 XMLHttpRequest 예제를 프로미스로 변환하면 다음과 같다.
const promiseGet = url => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.send();
xhr.onload = () => {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.response));
} else {
reject(new Error(xhr.status));
}
};
});
};
promiseGet("https://example.com/posts/1")
.then(res => console.log(res))
.catch(err => console.error(err))
.finally(() => console.log("BYE"));
프로미스 체이닝을 통해 콜백 헬 문제를 간결하게 해결할 수 있다.
Promise.resolve(1); // 즉시 resolve된 프로미스 생성
Promise.reject(new Error("에러")); // 즉시 reject된 프로미스 생성
const request1 = () => new Promise(resolve => setTimeout(() => resolve(1), 3000));
const request2 = () => new Promise(resolve => setTimeout(() => resolve(2), 2000));
const request3 = () => new Promise(resolve => setTimeout(() => resolve(3), 1000));
Promise.all([request1(), request2(), request3()])
.then(console.log) // [1, 2, 3]
.catch(console.error);
Promise.race([request1(), request2(), request3()])
.then(console.log) // 3
.catch(console.error);
가장 먼저 fulfilled 상태가 된 프로미스의 결과를 반환한다.
(단, 먼저 rejected 되면 그 에러를 즉시 반환)
Promise.allSettled([
new Promise(resolve => setTimeout(() => resolve(1), 2000)),
new Promise((_, reject) => setTimeout(() => reject(new Error("Error!!")), 1000)),
]);
/*
[
{ status: "fulfilled", value: 1 },
{ status: "rejected", reason: Error: Error!! }
]
*/
모든 프로미스의 처리 결과(성공/실패 포함)를 배열로 반환한다.