본문 바로가기
Javascript

비동기함수 순차 실행

by 맨날개발 2025. 1. 21.

🎈 비동기함수 순차 실행

비동기함수를 순차적으로 실행해야하는 경우가 존재한다. 다음과 같이 두가지 방법을 사용할 수 있다.

  1. for…of를 사용
  2. reduce를 사용한 방법

 

1️⃣ for…of를 사용한 방법

for...of 는 이터러블을 대상으로 순회하는 가장 일반적인 방법 중 하나이다. 이를 활용해서 직관적으로 코드를 구성할 수 있다.

 

아래와 같이 for…of를 사용하고 내부에서 await을 통해 순차적으로 실행할 수 있다.

function delay(time) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(`${time} 완료`);
      resolve(time);
    }, time);
  });
}

let i = 1;

for (const time of [3000, 2000, 1000]) {
  await delay(time, i++);
}

 

병렬이었다면 시간이 가장 짧은 1000부터 콘솔에 출력되어야 하지만, 위의 코드에서는 순차적으로 3000부터 2000, 1000 순으로 콘솔에 출력되는 것을 확인할 수 있다.

3000 완료
2000 완료
1000 완료

 

 

 

2️⃣ reduce 사용하기

다른 배열 메서드와 달리 reduce만이 순차적으로 실행 가능한 이유는 다음 순회가 실행되려면 이전 순회의 결과가 다음 순회의 인자로 전달되어야 하기 때문이다.

 

다른 메서드는 실행자체는 순서대로 호출이 되나 이전 순회가 다음 순회에 블로킹을 하지 않기 때문에 순차적 실행이 보장되지 않는다.

function delay(time) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(`${time} 완료`);
      resolve(time);
    }, time);
  });
}

let i = 1;

[3000, 2000, 100].reduce(async (acc, cur) => {
  await acc;
  return delay(cur, i++);
}, Promise.resolve());

 

콜백함수의 첫번째 파라미터(acc)await을 사용해야 순차적으로 호출이 되는 것을 확인할 수 있다.

 

순차적으로 3000부터 2000, 1000 순으로 콘솔에 출력되는 것을 확인할 수 있다.

3000 완료
2000 완료
1000 완료

 

 

📯 비동기의 병렬 실행의 결과를 순차 실행

비동기함수는 병렬로 실행을 하나 병렬 실행 된 결과는 순차적으로 실행하고 싶을 수 있다. 다음과 같이 두가지 방법을 사용할 수 있다.

  1. for await of
  2. Promise.all

 

1️⃣ for await of

for await of를 사용하면 비동기/동기 이터러블 객체 모두 사용이 가능하다. 동기 이터러블을 전달하는 경우 일반적인 for…of처럼 동작한다.

function delay(time) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(`${time} 완료`);
      resolve(time);
    }, time);
  });
}

for await (const value of [3000, 2000, 1000].map(delay)) {
  console.log('순회 순서 : ', value);
}

 

delay 함수는 병렬로 실행되기 때문에 가장 빠른 1000 → 2000 → 3000 순으로 실행이 되지만, 반복문 for 내부에서는 배열의 순서대로 출력되는 것을 확인할 수 있다.

1000 완료
2000 완료
3000 완료
순회 순서 :  3000
순회 순서 :  2000
순회 순서 :  1000

 

 

2️⃣ Promise.all

Promise.all에 프로미스 객체가 담긴 이터러블을 전달하는 경우, 모든 비동기 처리가 완료된 후 프로미스를 반환한다. 이터러블로 전달한 것 중 하나도 실행이 거부되지 않았다면 결과값에는 순회 완료 후 전달받은 값이 담긴 배열을 반환한다.

이 배열의 순서는 Promise.all에 전달한 이터러블의 순서를 보장한다.

function delay(time) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(`${time} 완료`);
      resolve(time);
    }, time);
  });
}

(async () => {
  const result = await Promise.all([3000, 2000, 1000].map(delay))
  
  result.forEach((time) => {
    console.log('순회 순서 : ', time);
  })
})();
1000 완료
2000 완료
3000 완료
순회 순서 :  3000
순회 순서 :  2000
순회 순서 :  1000
Promise.allSettled 또한 결과의 순서를 보장한다.