async/await를 사용한 비동기 loop 병렬로 순차처리하기

March 19, 2019

해당 포스팅은 https://lavrton.com/javascript-loops-how-to-handle-async-await-6252dd3c795/을 참고하였습니다.

ES2017부터 생성된 비동기 처리를 위한 async, await 처리는 이전의 callback 및 promise.then을 이용한 처리보다 훨씬 직관적인 비동기 처리 로직이 되었습니다. 개발을 진행하다 보면, 각 Array에 대한 item을 비동기 처리를 해야하는 경우가 생깁니다. 예를 들면, Array안의 내용을 loop를 돌며 ajax등을 처리하거나 하는 경우입니다.

async function processArray(array) {
  array.forEach(item => {
    await func(item);
  })
}

위의 example의 경우 syntax error를 반환하게 되는데, 이유는 forEach내의 익명함수에 async 키워드를 달아주어야, function내의 await 키워드를 사용할 수 있기 때문입니다.

async function processArray(array) {
  array.forEach(async (item) => {
    await func(item);
  })
}

forEach function의 인자로 실행할 익명함수에 async 키워드를 추가해주면, 해당 function은 비동기 처리로 await 키워드를 실행하게 됩니다. 그러나, forEach의 경우 해당 loop가 종료되는 것에 대한 결과를 기다려주지 않습니다. loop를 돌며 function내의 내용을 처리만 하기 때문에, 관련된 array를 병렬로 순차실행하는 것은 다르게 처리를 해줘야 합니다. 아래 내용은 이에 대한 비동기 처리의 병렬 처리에 대해 공유하고자 합니다.

1. setTimeout을 이용한 실행

forEach내의 function은 실행 이후 해당 값을 반환해주지 않기 때문에, 이의 경우는 setTimeout을 이용하여 실행을 지연시킴으로써 해결합니다. (좋은 방법은 아닙니다.)

function delay() {
  return new Promise(resolve => setTimeout(resolve, 300));
}

async function delayedLog(item) {
  await delay();
  console.log(item);
}
async function processArray(array) {
  array.forEach(async (item) => {
    await delayedLog(item);
  })
  console.log('Done!');
}

processArray([1, 2, 3]);

// output
Done!
1
2
3

이처럼 처리에 대한 지연을 setTimeout을 이용하여 처리하고 있습니다.

2. for...of 키워드 사용

for...of 키워드의 경우 ES2015에 제정된 내용으로써, Symbol 타입의 iterator 속성을 이용하며, for...of 내의 ...변수에 할당하도록 되어 있습니다. 이 기능을 이용하여, 해당 처리를 병렬로 처리할 수 있도록 사용하는 형태입니다.

async function processArray(array) {
  for (const item of array) {
    await delayedLog(item);
  }
  console.log('Done!');
}

// output
1
2
3
Done!

3. Promise.all을 이용

Promise 객체의 Promise.all() function을 사용합니다. 이전에 각 비동기처리를 위한 Promise를 반환하는 function을 Array의 고차함수인 map function으로 묶어서, Promise 배열을 반환하도록 만들고, 이를 all을 통해서 한번에 처리하도록 만들면, 훨씬 더 간략하게 Async array 처리를 병렬로 순차처리 할 수 있도록 할 수 있습니다.

async function processArray(array) {
  // map array to promises
  const promises = array.map(delayedLog);
  // wait until all promises are resolved
  await Promise.all(promises);
  console.log('Done!');
}
...