Promise 객체는 비동기 작업의 완료 또는 실패를 처리해줍니다.
이는 비동기 작업이 끝날 때 결과를 처리하여 콜백 지옥을 피하게 해주고 코드의 가독성과 유지보수성을 높여줍니다.
Table of Contents
Promise 기본 개념
이 객체는 세 가지 상태를 가집니다:
- pending: 아직 비동기 작업이 완료되지 않은 상태
- fulfilled: 비동기 작업이 성공적으로 완료된 상태
- rejected: 비동기 작업이 실패한 상태
객체가 생성되자마자 비동기 작업을 실행하고, then 또는 catch 메소드를 통해 작업의 성공 실패 결과를 처리할 수 있습니다.
Promise 생성자
객체 생성 시 executor function을 매개변수로 받습니다.
이 함수는 두 개의 인자를 받는데, 하나는 성공 시 호출할 resolve 함수이고, 다른 하나는 실패 시 호출할 reject 함수입니다.
const myPromise = new Promise((resolve, reject) => {
const isSuccess = true;
if (isSuccess) {
resolve("작업 성공!");
} else {
reject("작업 실패!");
}
});

위의 예제 코드를 개발자 도구를 통해 실행해 보면 위와 같은 결과를 얻을 수 있습니다.
then, catch, finally
- then(): 비동기 작업이 성공했을 때 실행
- catch(): 비동기 작업이 실패했을 때 실행
- finally(): 작업의 결과에 상관없이 항상 호출
const myPromise = new Promise((resolve, reject) => {
const isSuccess = true;
if (isSuccess) {
resolve("작업 성공!");
} else {
reject("작업 실패!");
}
});
myPromise
.then(result => {
console.log(result);
})
.catch(error => {
console.error(error);
})
.finally(() => {
console.log("비동기 작업 종료.");
});

예제 1: 비동기 작업 순차 실행
여러 비동기 작업을 순차적으로 실행하고 결과를 처리하고 싶을 때 Promise를 사용할 수 있습니다.
then() 메소드를 체이닝하면 순차적으로 실행시킬 수 있습니다.
function task1() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Task 1 완료");
resolve("Task 1 성공");
}, 1000);
});
}
function task2() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Task 2 완료");
resolve("Task 2 성공");
}, 1000);
});
}
task1()
.then(result => {
console.log(result);
return task2();
})
.then(result => {
console.log(result);
});

위의 스크립트에서는 task1()을 먼저 실행하고 1초 뒤 console.log() 메소드를 통해 콘솔에 메시지를 찍습니다.
그럼 첫 번째 then()이 실행되어 task1()의 결과를 출력하고 리턴 값으로 다른 Promise인 task2()를 실행합니다.
task2()가 완료되면 두 번째 then()이 실행되고 이로서 비동기 작업이 순차적으로 실행되게 됩니다.
예제 2: 비동기 작업 병렬 실행
Promise.all()을 사용하면 여러 비동기 작업을 병렬로 실행하고, 모든 작업이 완료된 후 결과를 처리할 수 있습니다.
function task1() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Task 1 완료");
resolve("Task 1 성공");
}, 1000);
});
}
function task2() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Task 2 완료");
resolve("Task 2 성공");
}, 500);
});
}
Promise.all([task1(), task2()])
.then(results => {
console.log("모든 작업 완료:", results);
})
.catch(error => {
console.log("에러 발생:", error);
});

순차 실행이 아닌 병렬 실행이라 task1()과 task2() 중 빠르게 종료된 작업이 먼저 실행되는 것을 확인할 수 있습니다.
또한 지정한 모든 작업이 끝나야 then() 메소드가 실행되는 것도 알 수 있습니다.
예제 3: 병렬 실행 시 하나라도 실패하면 모두 실패
Promise.all은 배열 내의 어느 하나라도 실패하면 전체가 실패한 것으로 간주합니다.
function task1() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Task 1 완료");
resolve("Task 1 성공");
}, 1000);
});
}
function task2() {
return new Promise((_, reject) => {
setTimeout(() => {
console.log("Task 2 실패");
reject("Task 2 실패");
}, 500);
});
}
Promise.all([task1(), task2()])
.then(results => {
console.log("모든 작업 완료:", results);
})
.catch(error => {
console.log("에러 발생:", error);
});

이번 예제에서는 먼저 끝나는 task2()가 실패하여 Promise.all()은 나머지 작업의 종료를 기다리지 않고 바로 catch() 메소드를 호출했습니다.
예제 4: 가장 먼저 완료된 작업만 처리
같은 결과를 원하지만 이를 해결하기 위해 여러가지 방법을 시도해야하는 경우가 있습니다.
이때 Promise.race를 쓸 수 있는데, 이는 여러 비동기 작업 중 가장 먼저 완료된 작업의 결과만 반환합니다.
function task1() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Task 1 완료");
resolve("Task 1 성공");
}, 1000);
});
}
function task2() {
return new Promise(resolve => {
setTimeout(() => {
console.log("Task 2 완료");
resolve("Task 2 성공");
}, 500);
});
}
Promise.race([task1(), task2()])
.then(result => {
console.log("가장 먼저 완료된 작업:", result);
})
.catch(error => {
console.log("에러 발생:", error);
});

Promise 정리
이 객체는 비동기 작업을 효율적으로 처리할 수 있는 강력한 도구입니다.
then, catch, finally와 같은 메소드를 사용하여 비동기 작업의 흐름을 제어할 수 있습니다.
또한 Promise.all, Promise.race 등을 통해 여러 작업을 동시에 처리하거나 가장 먼저 완료된 작업을 다룰 수 있습니다.