Karolina Kaszuba Personal Dev Blog

Using async/await with Array.forEach() does not work as expected

June 12, 2021

Using async/await with Array.forEach() does not work as expected

You probably came across the need to loop over an array while using async/await, and the Array.forEach() method seems like an easy choice. The problem is that “await” in a forEach loop does not seem to work. Here is a simple example:

// function returning a promise:
const returnSth = (x) => {
  return new Promise((resolves, rejects) => {
    setTimeout(() => {
      resolves(x);
    }, 1000);
  });
};

// example with a forEach
const exampleOne = () => {
  const arr = [1, 2, 3, 4];
  arr.forEach(async (item) => {
    const result = await returnSth(item);
    console.log(result);
  });
  console.log("after");
};

exampleOne();
// output: after 1 2 3 4
Reminder » Array.forEach() method executes a provided callback function once for each array element. Unlike Array.map() or Array.reduce() it always returns the value undefined and therefore is not chainable.

You could expect such a result: 1 2 3 4 after but it is not. In this case “after” is logged immediately, and all the numbers after 1 second, long after the exampleOne() function execution finished. The quick fix is to use a for...of statement to loop over the array. This way each iteration waits for the returnSth() function execution to finish. It happens in a sequential order.

// example with a for...of loop
const exampleTwo = async () => {
  const arr = [1, 2, 3, 4];
  for (const item of arr) {
    const result = await returnSth(item);
    console.log(result);
  }
  console.log("after");
};

exampleTwo();
// output: 1 2 3 4 after

The console output is that each number is displayed one by one, after a second and another second and another, and then after the last number, “after” is displayed immediately. The reason why this is such a different behaviour is that Array.forEach() expects a synchronous function, and it simply does not wait for promises. It just calls the function, not waiting for the result. You can read more about the Array.forEach() method in the MDN docs.

If you don’t want to run each iteration sequentially, and you need to run them in parallel, there is another solution. Each of the async callback function calls returns a promise that we don’t want to throw away, so we can use Array.map() method instead of Array.forEach(). With Promise.all you can wait for all the promises to resolve before continuing to the next line of code.

// example with Promise.all and map
const exampleThree = async () => {
  const arr = [1, 2, 3, 4];
  await Promise.all(
    arr.map(async (item) => {
      const result = await returnSth(item);
      console.log(result);
    })
  );
  console.log("after");
};

exampleThree();
// output: 1 2 3 4 after

This way you get all the logs after around one second. As you can see there are different solutions to the async/await problem with Array.forEach() method, and you can choose whichever solves your problem best.


Profile picture

Written by Karolina Kaszuba - Software Developer and Designer who lives and works in a beautiful city of Gdańsk, Poland.