For more tips like this, sign up to the weekly newsletter!

How to use forEach with async/await

A synchronous for loop works similarly to a forEach for blocking operations.

These two do the same:

const arr = [100, 70, 50];

for(let i of arr) {
  console.log(i); // 100, 70, 50
}

arr.forEach((i) => {
  console.log(i); // 100, 70, 50
})

But as soon as async comes into play, this behavior changes:

for(let i of arr) {
  await wait(i);
  console.log(i);
}
console.log("done");

// 100, ~100 ms later
// 70, ~70 ms later
// 50, ~50 ms later
// done, after that

But with forEach, it works differently:

arr.forEach((i) => {
  wait(i);
  console.log(i);
})
console.log("done");

// 100, instantly
// 70, instantly
// 50, instantly
// done, instantly

While the former waits the appropriate amount of time between printing the numbers, the latter does not wait at all.

Let's await

Oh wait, there is no await for the wait!

Let's fix this:

arr.forEach(async (i) => {
  await wait(i);
  console.log(i);
})
console.log("done");
// done, instantly
// 50, ~50 ms later
// 70, ~20 ms later
// 100, ~30 ms later

It does not wait for completion, which is still not the same as with the for..of loop.

Wait for completion

To fix this, as it turns out, we need to change the forEach to a map, and wait for all the Promises using Promise.all:

await Promise.all(arr.map(async (i) => {
  await wait(i);
  console.log(i);
}))
console.log("done");
// 50, ~50 ms later
// 70, ~20 ms later
// 100, ~30 ms later
// done, after that
Run sequentially

One last difference is that the elements are run simultaneously. To change it to sequential execution, even the Promise.all + map combo falls short, and we need to use a different structure.

This solution uses a reduce:

await arr.reduce(async (prev, i) => {
  await prev;
  await wait(i);
  console.log(i);
}, Promise.resolve());
console.log("done");

// 100, ~100 ms later
// 70, ~70 ms later
// 50, ~50 ms later
// done, after that

The code above faithfully emulates the for..of loop we started with.

Try it
References
Learn more: