JavaScript Looping with async await


Example

When using async await in loops, you might encounter some of these problems.

If you just try to use await inside forEach, this will throw an Unexpected token error.

(async() => {
 data = [1, 2, 3, 4, 5];
 data.forEach(e => {
   const i = await somePromiseFn(e);
   console.log(i);
 });
})();

This comes from the fact that you've erroneously seen the arrow function as a block. The await will be in the context of the callback function, which is not async.
The interpreter protects us from making the above error, but if you add async to the forEach callback no errors get thrown. You might think this solves the problem, but it won't work as expected.

Example:

(async() => {
  data = [1, 2, 3, 4, 5];
  data.forEach(async(e) => {
    const i = await somePromiseFn(e);
    console.log(i);
  });
  console.log('this will print first');
})();

This happens because the callback async function can only pause itself, not the parent async function.

You could write an asyncForEach function that returns a promise and then you could something like await asyncForEach(async (e) => await somePromiseFn(e), data ) Basically you return a promise that resolves when all the callbacks are awaited and done. But there are better ways of doing this, and that is to just use a loop.

You can use a for-of loop or a for/while loop, it doesn't really matter which one you pick.

(async() => {
  data = [1, 2, 3, 4, 5];
  for (let e of data) {
    const i = await somePromiseFn(e);
    console.log(i);
  }
  console.log('this will print last');
})();

But there's another catch. This solution will wait for each call to somePromiseFn to complete before iterating over the next one.
This is great if you actually want your somePromiseFn invocations to be executed in order but if you want them to run concurrently, you will need to await on Promise.all.

(async() => {
 data = [1, 2, 3, 4, 5];
 const p = await Promise.all(data.map(async(e) => await somePromiseFn(e)));
 console.log(...p);
})();

Promise.all receives an array of promises as its only parameter and returns a promise. When all of the promises in the array are resolved, the returned promise is also resolved. We await on that promise and when it's resolved all our values are available.

The above examples are fully runnable. The somePromiseFn function can be made as an async echo function with a timeout. You can try out the examples in the babel-repl with at least the stage-3 preset and look at the output.

function somePromiseFn(n) {
 return new Promise((res, rej) => {
   setTimeout(() => res(n), 250);
 });
}