promise.all / promise.allSettled 차이

2021. 3. 4. 22:35공부내용 공유하기

비동기 처리를 위해 자주 사용되는 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.allSettled

The allSettled function requires its this value to be a constructor function that supports the parameter conventions of the Promise constructor.

tc39.es


 

프로미스의 병렬처리를 위해 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를 사용하는게 더 좋겠다.

 

두 메서드가 사용법은 유사하지만 반환 값이나 에러 핸들링 방식이 완전히 다르기 때문에 그 점을 유념하고 사용하면 좋을 것 같다.