하루살이 개발일지

[JavaScript] Promise.allSettled vs. Promise.all 본문

웹개발/JavaScript

[JavaScript] Promise.allSettled vs. Promise.all

harusari 2024. 7. 29. 01:00

기존에 Promise.all은 알고 있었지만 병렬 처리를 위해 Promise.all을 사용하던 도중 개별 프로미스들의 성공과 실패를 판단해 실패한 프로미스에 대한 처리를 하고 싶었는데, 해당 메서드가 가진 한계로 인해 원하는 기능을 구현할 수 없었다. 와중에 병렬 처리를 해주는 Promise.allSettled라는 메서드를 알게 되었고 이 두 가지의 차이점을 비교해보고 싶었다.

 

Promise.all


 

우선 Promise.all은 모든 프로미스가 성공적으로 완료(fulfill)될 때까지 기다린 후 모든 프로미스의 반환 값을 배열에 넣어 반환한다. 하나의 프로미스라도 실패하면 전체가 실패로 간주되고, 개별 프로미스의 성공 또는 실패를 추적할 수 없다.

 

즉 모든 프로미스 중 하나라도 reject가 호출된다면, 성공한 프로미스 응답은 무시된 채 catch문으로 빠져버린다.

 

https://dmitripavlutin.com/promise-all/

 

const a = new Promise((resolve, reject) => {
  setTimeout(() => resolve('a 성공'), 1000);
});

const b = new Promise((resolve, reject) => {
  setTimeout(() => resolve('b 성공'), 2000);
});

const c = new Promise((resolve, reject) => {
  setTimeout(() => reject('c 실패'), 1500);
});

Promise.all([a, b, c])
  .then(results => {
    console.log('이 부분은 실행되지 않습니다:', results);
  })
  .catch(error => {
    console.log('이 부분만 실행됩니다:', error);
  });

// Promise.all의 반환값 예시
// 성공한 경우: ['a 성공', 'b 성공', 'c 성공']
// 실패한 경우: 'c 실패' (첫 번째 실패한 이유)

 

즉, 위 예시에서 비동기 작업인 a와 b가 성공해도, c가 실패하게 되면 catch문으로 빠져버리게 되어 개별 프로미스의 성공과 실패를 추적할 수 없게 된다.

 

 

멱등성이란?

멱등성(idempotence)이란, 연산을 여러 번 수행해도 결과가 달라지지 않는 성질을 말한다. 즉 같은 요청을 여러 번 보내도 동일한 결과가 반환된다면 멱등성이 있다고 할 수 있다. 예를 들어, 특정 리소스를 조회하는 API 요청은 멱등성을 보장하는 경우가 많다. 이러한 요청을 여러 번 보내도 서버에서 반환하는 리소스는 변하지 않는다.

Promise.all을 사용할 때, 멱등성이 보장된다면 Promise.all을 사용해도 괜찮다. 예를 들어, 여러 API 요청을 병렬로 수행하고자 하는 경우에 해당 API가 멱등성을 보장한다면, 요청이 실패한 경우에도 다시 시도할 수 있다. 이는 실패한 요청을 다시 보내더라도 서버의 상태나 결과가 변하지 않기 때문이다.

그러나 멱등성이 보장되지 않는 경우, 즉 같은 요청을 여러 번 보낼 때마다 결과가 달라질 수 있는 경우에는 Promise.all을 사용하는 것이 바람직하지 않을 수 있다. 예를 들어, 서버에 데이터를 업데이트하거나 삭제하는 API 요청은 멱등성이 보장되지 않을 수 있다. 이런 경우, 실패한 프로미스로 인해 다른 프로미스들이 중단되면, 서버 상태가 일관되지 않게 될 수 있다. 따라서 멱등성이 보장되지 않는 경우에는 각각의 프로미스를 개별적으로 처리하거나, 실패한 요청을 별도로 관리하는 방법을 고려해야 한다.

 

Promise.allSettled


 

Promise.allSettled는 각 프로미스가 성공 또는 실패한 후에 결과를 반환한다. 이를 통해 개별 프로미스의 상태를 추적하고 실패한 프로미스를 별도로 처리할 수 있다. 이는 서로 종속되지 않은 독립적인 작업이 있을 때 유용하다.

 

Promise.allSettled의 반환값은 배열로 받은 모든 프로미스의 fulfill, reject 여부와 상관 없이, 전부 완료되었다면 (pending 상태에서 벗어난) 프로미스들의 결과를 배열로 리턴한다.

 

https://dmitripavlutin.com/promise-all-settled/

 

 

앞서 Promise.all로 실행했던 예시 코드를 Promise.allSettled로 바꾸면 다음과 같다.

const a = new Promise((resolve, reject) => {
  setTimeout(() => resolve('a 성공'), 1000);
});

const b = new Promise((resolve, reject) => {
  setTimeout(() => resolve('b 성공'), 2000);
});

const c = new Promise((resolve, reject) => {
  setTimeout(() => reject('c 실패'), 1500);
});

Promise.allSettled([a, b, c])
  .then(results => {
    console.log('모든 작업의 결과:', results);
  });

// Promise.allSettled의 반환값 예시
// [
//   { status: 'fulfilled', value: 'a 성공' },
//   { status: 'fulfilled', value: 'b 성공' },
//   { status: 'rejected', reason: 'c 실패' }
// ]

 

확인해 볼 수 있는 점은, 배열 안에 각각의 프로미스들의 status와 value를 담은 객체를 반환해주고 있다. 프로미스 성공 시 status 필드에 fulfilled 값을 넣고, 실패하면 rejected 값을 넣어준다. 이제 실패한 프로미스들만 따로 관리할 수 있게 된 것이다.

 

 

그 외의 특성

  • 소요 시간 : Promise.all과 Promise.allSettled는 병렬로 여러 프로미스를 실행하기 때문에, 최대 소요 시간은 처리 시간이 가장 긴 프로미스의 시간이 된다.
  • Promise.allSettled 폴리필 : Promise.allSettled는 ES11에서 도입된 기능으로, 구형 브라우저나 LTS버전 Node.js를 사용한다면 폴리필을 따로 구현해야 할 수 있다.
  • Promise.all과 Promise.allSettled는 모두 병렬 처리를 위한 것이기 때문에 순차적 실행이 필요한 상황이라면 reduce를 사용하는 것이 적절하다. 

 

 


reference

https://baek.dev/post/48/

https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%8D%94%EC%9D%B4%EC%83%81-Promiseall-%EC%93%B0%EC%A7%80%EB%A7%90%EA%B3%A0-PromiseallSettled-%EC%82%AC%EC%9A%A9%ED%95%98%EC%9E%90