[JS] 반복문 안에서의 비동기 함수 사용

2023. 2. 7. 18:43
반응형

반복문 안에서의 비동기 함수 사용

 

자바스크립트로 프로그래밍을 하다 보면 비동기 함수를 종종 반복문을 돌려야 할 상황이 있다.

한 가지 케이스로는 하나는 데이터베이스에 Object 객체 리스트를 동시에 Insert하는 경우가 있을 수 있는데 여기서 흔히 실수할 수 있는 경우를 찾아보고, 적절한 방법을 찾아보았다.

 


const insertDatabase = (data) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("Insert Database : " + data);
      resolve();
    }, 1000);
  });
};

const data = ["Apple", "Banana", "Carrot", "Donut"];

let start = new Date();
await func();
let end = new Date();
console.log("During time : " + (end - start));

 

실제 데이터베이스에 넣는다고 가정하고 만든 1초가 걸리는 비동기 함수와 데이터 리스트들을 만들었다.

그리고 아래의 함수를 실행시키고 시간을 재보기로 하자

 

 


1. forEach()

const func = async () => {
  data.forEach(async (x) => {
    await insertDatabase(x);
  });
  console.log("Complete");
};

 

아무래도 객체 리스트를 순회하는 데에 가장 많이 쓸 것 같은 forEach문이다.

하지만 해당 코드는 문제가 있다.

CompleteDuring time : 3
Insert Database : Apple
Insert Database : Banana
Insert Database : Carrot
Insert Database : Donut

 

위와 같이 Promise가 resolve되는 것을 기다려주지 않고 다음 코드가 진행된다.

따라서 DB에 Insert가 되기 전에 다음 로직들이 실행되는 문제를 초래할 수 있다.

이는 Javascript의 forEach()는 비동기 함수를 기다려주지 않기 때문이다. 

 

 


2. for & for of

const func = async () => {
  for (let i = 0; i < data.length; i++) {
    await insertDatabase(data[i]);
  }
  console.log("Complete");
};
const func = async () => {
  for (let i of data) {
    await insertDatabase(i);
  }
  console.log("Complete");
};

위의 로직을 for문과 for of문으로 변경했을 때이다. 이 때의 실행 결과를 먼저 보자.

 

Insert Database : Apple
Insert Database : Banana
Insert Database : Carrot
Insert Database : Donut
Complete
During time : 4013

 

forEach문과는 다르게 데이터들이 Insert가 완료되기를 기다린 이후 다음 코드들이 실행되었다!! 잘 된것 같지만 한가지를 짚고 가보자. 

실행시간이 4초 가까이 걸린 것을 확인하였다. 한 번에 하나씩 실행 완료되기를 기다리고 다음 것이 실행되었음을 알 수 있다.

 

직렬적으로 로직이 처리되고 있다고 볼 수 있다. 용도에 따라 사용할 수도 있겠지만 데이터베이스에 리스트를 Insert할때 굳이 하나씩 넣고 기다릴 필요가 있겠는가 하는 의문이 생긴다.

 

 

 


3. Promise.All()

const func = async () => {
  const promises = [];
  for (let i of data) {
    const promise = insertDatabase(i);
    promises.push(promise);
  }
  await Promise.all(promises);
  console.log("Complete");
};

앞에서 Promise에 대해 다룬 적이 있었는데 Promise.all(Promise[])는 해당 인자의 프로미스 객체들이 모두 완료될 때 까지 기다리며 각각 프로미스들의 Resolve값의 배열을 반환하는 메서드이다.

해당 메서드를 활용하여 각각의 데이터들의 프로미스 객체들을 만들고, 모두 끝날 때 까지 대기하게 하였다.

 

Insert Database : Apple
Insert Database : Banana
Insert Database : Carrot
Insert Database : Donut
Complete
During time : 1006 

 

1초 가량이 걸렸다. 즉 각각의 비동기 함수들이 병렬적으로 실행되었음을 확인하였고, 뒤의 로직들도 비동기 함수들이 끝난 후에 실행되는 것을 알 수 있었다.

 

 


4. 정리

반복문 안에서 비동기 함수를 사용할 때에는 아래의 방법들이 있다.

  • forEach()는 사용할 수 있는 경우를 신중하게 고려해보자
  • for, for in은 직렬적으로 처리한다
  • Promise.All()은 병렬적으로 처리한다
반응형

BELATED ARTICLES

more