The problem with using async/await in a forEach loop in JavaScript is the fact that you can’t wait for the promise resolution. Let’s see how you can fix it.

Why async/await doesn’t work with forEach in JavaScript

So, you’re calling an async function in the forEach loop, and you noticed that the next iteration of the loop wouldn’t awaitfor the async function to return the result.

Imagine you have a function getUsers that returns the list of users. This list, however, doesn’t contain the user details, which you’d like to print to the console.

So, you write the forEach and try to get the details for every user with the function getDetails.

const users = await getUsers();

users.forEach(user => {
  const details = user.getDetails();
  console.log(details);
})

The first go is a mess as you just got 3 pending promises.

Promise { <pending> }
Promise { <pending> }
Promise { <pending> }

Of course, you think to yourself. user.getDetails is an async function and all async functions return Promises.

So you go ahead and try to fix the issue by adding the await keyword.

const users = await getUsers();

users.forEach(user => {
  const details = await user.getDetails();
  console.log(details);
})

You run the code, but there’s another error.

SyntaxError: await is only valid in async function

Of course, you realise, the function passed into forEach should be marked as async when you use await in it.

One more fix.

const users = await getUsers();

users.forEach(async (user) => {
  const details = await user.getDetails();
  console.log(details);
})

Another run, but to no avail. You stare at an empty console, having no idea why there’s no output.

3 viable solutions to the async/await problem and the forEach on JS array

You can resolve the issue with async/await in the forEach JavaScript loop by rewriting your code into the plain for loop.

const users = await getUsers();

for (let i = 0; i < users.length; i++) {
  const details = await user.getDetails();
  console.log(details);
}

Or, you can achieve the same outcome with the for ... of JS loop.

const users = await getUsers();

for (const user of users) {
  const details = await user.getDetails();
  console.log(details);
}

If you don’t like simple code, then you can do a clever trick by using map instead of forEach.

const users = await getUsers();

const userDetailsPromises = users.map(user => user.getDetails());
const userDetails = await Promise.all(userDetailsPromises);

console.log(userDetails);

Let’s take a look step-by-step at what’s happening in the example above:

  1. Nothing new in the first line, we get an array of users and store it in the users variable.
  2. Then, we iterate over the array of users with map, calling getDetails on every user.
  3. As we need to await for the resolution of every Promise in the userDetailsPromises array, we pass it to Promise.all and add the await keyword in front of it.
  4. Eventually, we log the array of userDetails to the console.

This code can be simplified further by combining the second, and the third lines.

const userDetails = await Promise.all(users.map(user => user.getDetails()));