page-cover
개발
자바스크립트 비동기 작업의 병렬, 순차 실행 톺아보기
sooros5132-avatarsooros513211/8/2023JavaScript Promise
순서를 보장해서 반복문을 사용할 일이 생겼는데 순서가 어떻게 되는지 헷갈려서 정리를 해보고자 한다.
많이 사용하는 3가지 방식이다. 먼저 결과를 정리하자면 아래와 같고 실험에 실행한 코드도 적어두었다.
순차/병렬
비동기 작업들을 기다릴 수 있는가??(블로킹)
for await
순차
forEach
병렬
아니오
Promise.all
병렬
공통으로 사용할 createWork와 10개의 작업이 들어있는 works 배열이다.
javascript
function createWork(callback, delay) {
  return function () {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(callback());
      }, delay);
    });
  };
}

// 0 ~ 1초가 걸리는 작업 10개가 들어있는 배열이다.
const works = [...new Array(10)].map((_, i) =>
  createWork(function () {
    console.log(i + 1, "for await");
  }, Math.random() * 1000)
);
아래 실험들은 start, done이 언제 출력되는지, works가 순서대로 처리가 되는지를 보면 된다.

for await

await를 안붙인채 work를 실행한다.
javascript
for await (const work of works) {
  work();
}
work를 실행할 때 await가 안붙었으니 바로 done이 출력이 됐고 work는 병렬 처리로 된다.
이 경우 일반 for of처럼 작동을 함.
javascript
for await (const work of works) {
  await work();
}
await가 붙어서 순차 실행이 되고 작업이 끝나기를 기다린다. done이 마지막에 출력 됨

forEach

forEach로 비동기 작업들을 기다리려고 하는 것은 많은 사람들이 겪는 실수이다. 작업들이 완료되길 기다릴 것 같이 생겼지만 실제론 그렇지 않다.
javascript
works.forEach(async (work) => {
  await work();
});
forEach는 비동기, 병렬 처리로 된다.
작업들을 기다리지 않고 다음 라인의 코드인 done메시지가 출력됐다.
작업들을 기다려야 할 상황이라면 forEach가 아닌 map + Promise.all을 사용해야 한다.

Promise.all

javascript
const promises = works.map(async (work) => await work());

await Promise.all(promises);
Promise.all은 병렬로 실행되고 비동기 작업을 기다릴 수 있다.