비동기 처리를 위해 자주 사용되는 Promise.
Promise는 ES6부터 자바스크립트에 본격적으로 적용되어 현재는 대부분의 개발자들에게 친숙한 개념이다.
보통 Promise는 async / await 문법과 사용되고, 특정 상황에서 유용하게 쓰이는 내장 메서드들을 가지고 있다.
오늘은 그 중에서 자칫 혼동되기 쉬운 Promise.all과 Promise.allSettled의 차이점을 알아보도록 하자.
Promise.all
Promise.all([ promise1, promise2, ... ]) 의 형태로 사용되며, 배열로 받은 모든 프로미스가 fulfill 된 이후, 모든 프로미스의 반환 값을 배열에 넣어 반환한다. 이때 배열에 들어있는 값의 순서는 완료 순서와 상관없이, 인자로 받은 프로미스 배열의 순서와 일치한다.
만약 배열에 있는 프로미스 중 하나라도 reject가 호출된다면, Promise.all 역시 reject를 호출하고 가장 먼저 reject된 프로미스의 거부사유(reason)를 사용한다.
반환 값 :
- 모두 성공(fulfill) 시 : 각 프로미스에서 반환된 값들의 배열
- 하나라도 실패시 : 처음으로 reject 된 프로미스의 이유(reason)를 가지고 reject 됨
Promise.allSettled
Promise.allSettled([ promise1, promise2,...]) 의 형태로 Promise.all과 동일한 형태로 실행한다.
하지만 반환값은 Promise.all과 매우 다르다.
배열로 받은 모든 프로미스의 fulfilled, reject 여부와 상관없이, 전부 완료만 되었다면(not pending) 해당 프로미스들의 결과를 배열로 리턴한다.
반환 값 :
- 모두 완료(not pending) 시 : 각 프로미스들의 결과를 담은 배열 반환
프로미스의 병렬처리를 위해 Promise.all를 사용하는 경우, 프로미스들의 부분적인 실패/성공에는 대응할 수 없다. 배열 내부의 어떤 promise라도 reject 되었다면, 완료된 프로미스의 데이터 역시 버려지는 것이다.
테스트 코드를 통해 두 메소드의 차이를 확인해보자.
all.js
const promise1 = Promise.resolve(["3"]);
const promise2 = new Promise((resolve, reject) =>
setTimeout(reject, 2400, "foo")
);
const test = async () => {
try {
return await Promise.all([promise1, promise2]);
} catch (e) {
throw e;
}
};
const testResult = test();
testResult.then((res) => {
console.log("res:", res);
}).catch((error) => {
console.log("error:", error);
});
result
allSettled.js
const promise1 = Promise.resolve(["3"]);
const promise2 = new Promise((resolve, reject) =>
setTimeout(reject, 2400, "foo")
);
const test = async () => {
try {
return await Promise.allSettled([promise1, promise2]);
} catch (e) {
throw e;
}
};
const testResult = test();
testResult.then((res) => {
console.log("res:", res);
}).catch((error) => {
console.log("error:", error);
});
result
["3"] 을 가지고 바로 resolve를 호출하는 프로미스 1, 2.4초 후에 "foo"라는 reason을 가지고 reject를 호출하는 프로미스 2
두 개의 프로미스를 가지고 테스트한 결과는 다음과 같다.
Promise.all은 프로미스 1이 바로 resolve 되었음에도 2.4초 후 프로미스 2의 reject를 받고 그대로 reason을 반환한다.
Promise.allSettled는 프로미스 1,2 가 모두 종료되길 기다린 후, 각 프로미스의 결괏값을 배열로 만들어 반환한다.
반환되는 결괏값의 구조는 다음과 같다.
{
status : "fulfilled" | "rejected";
value? : any;
reason? : any;
}
status가 fulfilled면 value, rejected면 reason이 존재한다.
서로 의존적인 프로미스들을 병렬적으로 실행할 경우, Promise.all을 사용하는 게 옳다.
하지만 의존적이지 않은 프로미스들을 병렬적으로 실행하려는 경우, Promise.allSettled를 사용하는게 더 좋겠다.
두 메서드가 사용법은 유사하지만 반환 값이나 에러 핸들링 방식이 완전히 다르기 때문에 그 점을 유념하고 사용하면 좋을 것 같다.