FrontEnd/HTML5 & CSS & JavaScript

[JavaScript] reduce를 사용하여 순차적으로 프로미스 실행시키기

푸고배 2023. 1. 28. 22:39

Promise.all()

Promise.all()여러 개의 비동기 작업을 병렬(순서를 보장하지 않음)로 실행시키는 경우 사용된다.
여러 개를 동시에 실행하고 가장 마지막 작업이 완료될 때 완료 상태의 프로미스를 반환한다.

const myPromises = [
  new Promise((resolve) => setTimeout(() => {resolve('A (slow)'); console.log('A (slow)')}, 1000)),
  new Promise((resolve) => setTimeout(() => {resolve('B (slower)'); console.log('B (slower)')}, 2000)),
  new Promise((resolve) => setTimeout(() => {resolve('C (fast)'); console.log('C (fast)')}, 10))
];

Promise.all(myPromises).then(console.log)

// C (fast) 
// A (slow) 
// B (slower)

위의 코드를 보면, 각각 1초, 2초, 0.01초 딜레이 후 콘솔을 출력하는 프로미스 A, B, C가 있다.
각 작업은 A, B, C 순으로 병렬 실행되지만, 작업에 걸리는 시간으로 예상할 수 있듯이 C, A, B 순으로 콘솔 로그가 찍히게 된다.
다시말해, Promise.all()은 작업의 시작 순서가 완료의 순서를 보장하지 않는다.

따라서, 실행할 비동기 작업들의 순서가 중요하지 않은 경우에만 Promise.all을 사용해야 한다.
ex) 여러 데이터를 패칭하는 경우

그리고 하나의 Promise라도 거절(rejected) 상태를 가진다면, Promise.all()은 에러(.catch)로 반환된다는 특징도 있다.

하지만, 여러 개의 비동기 작업을 순서대로 실행시키고 싶은 경우가 있다.

Array.prototype.reduce()

배열.reduce((누적값, 현잿값, 인덱스, 요소) => { return 결과 }, 초깃값);

reduce 사용의 대표적인 예는 아래와 같은 누적합 구하기이다.

const array1 = [1, 2, 3, 4];

// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
  (accumulator, currentValue) => accumulator + currentValue,
  initialValue
);

console.log(sumWithInitial);
// Expected output: 10


이전의 연산값(누적값)에 현재값을 연산하여 다음 연산을 위해 결과를 반환시킨다.
reduce 로직을 이용하면, 여러 개의 프로미스를 순서를 보장하며 실행시킬 수 있다.

const myPromises = [
  new Promise((resolve) => setTimeout(() => {resolve('A (slow)')}, 1000)),
  new Promise((resolve) => setTimeout(() => {resolve('B (slower)')}, 2000)),
  new Promise((resolve) => setTimeout(() => {resolve('C (fast)')}, 10))
];

myPromises.reduce((promise, currentPromise) => {
  return promise.then(async() => {
    const result = await currentPromise;
    console.log(result); // 순서를 보장해야하는 작업 수행
    return result;
  });
}, Promise.resolve());

// A (slow) 
// B (slower) 
// C (fast)

Promise.all의 코드를 reduce를 사용하여 Promise를 순차적으로 수행(A->B->C)하는 코드로 수정해보았다.

- 초기값 : Promise.resolve() [빈 resolve]
- 누적값(promise) : 이전 Promise가 성공했을 때, 현재 Promise를 반환
- 현재값(currentPromise) : myPromises의 각 요소

초기값으로 빈 resolve를 주고, 바로 다음 단계에서 resovle의 콜백에 현재 프로미스를 반환하도록 만든다.
그러면 두 번째 단계에선느 누적값으로 이전 프로미스(A)를 갖고 있게되고, 이전 프로미스(A)의 콜백을 통해 현재 프로미스(B)를 반환하게 한다. 이 때, 현재 Promise(B)를 반환하기 전에, 현재 Promise(B)의 콜백을 이용해서 프로미스 내부 작업(위의 예시 코드에서는 console 로그 찍기)를 수행하면 순서를 보장할 수 있다. 이 작업을 반복함으로써, 프로미스를 순차적으로 실행시킬 수 있는 코드가 완성된다.


Reference

javascript - How do I reduce multiple Promise.all?

I am trying to use a Promise.all inside of a reduce and cannot get my function to work, unless there is only one user in my array. The starting object of the reduce is a Promise. The first time t...

stackoverflow.com

[javascript] Promise.all : 해결 된 값의 순서 - 리뷰나라

보면 MDN 것은 그것은처럼 보이는 values에 전달 then()Promise.all의 콜백 약속의 순서대로 값이 포함되어 있습니다. 예를 들면 다음과 같습니다. var somePromises = [1, 2, 3, 4, 5].map(Promise.resolve); return Promise.

daplus.net

비동기 작업을 순차대로 실행시키기

비동기 작업을 동기적으로 순차대로 실행을 시켜야 할 때가 존재합니다.Promise.all 을 이용하면 배열에 담긴 비동기 작업을 배열의 순서대로 실행은 시켜주지만, 실행의 완료를 기다려 주지는 않

velog.io

Promise.all 로 비동기 로직 개선하기

아주 오랜 옛날부터, 전설로 내려오는 코드가 있습니다. (3학년 1학기에 수행했던 산학프로젝트 코드입니다.) 이 코드는 간단한 그룹 매칭 서비스에서 그룹을 생성하는 역할인데요, 여러 비동기

merrily-code.tistory.com

https://www.zerocho.com/category/JavaScript/post/5acafb05f24445001b8d796d

www.zerocho.com

반응형