Are there any issues with using async/await in a forEach loop? I'm trying to loop through an array of files and await on the contents of each file.
import fs from "fs-promise";

async function printFiles() {
  const files = await getFilePaths(); // Assume this works fine

  files.forEach(async (file) => {
    const contents = await fs.readFile(file, "utf8");
    console.log(contents);
  });
}

printFiles();
This code does work, but could something go wrong with this? I had someone tell me that you're not supposed to use async/await in a higher-order function like this, so I just wanted to ask if there was any issue with this.

Great question. Should be bountied!

2021-03-09 • LilYatty

I really need an answer for this!

2021-03-10 • floribella69 Expert User

Expert  Expert Medal

Sure the code does work, but I'm pretty sure it doesn't do what you expect it to do. It just fires off multiple asynchronous calls, but the printFiles function does immediately return after that.

Reading in sequence

If you want to read the files in sequence, you cannot use forEach indeed. Just use a modern for … of loop instead, in which await will work as expected:

async function printFiles() {
  const files = await getFilePaths();

  for (const file of files) {
     const contents = await fs.readFile(file, "utf8");
     console.log(contents);
  }
}

Reading in parallel

If you want to read the files in parallel, you cannot use forEach indeed. Each of the async callback function calls does return a promise, but you're throwing them away instead of awaiting them. Just use map instead, and you can await the array of promises that you'll get with Promise.all:

async function printFiles() { 
  const files = await getFilePaths(); 
  await Promise.all(files.map(async (file) => {
     const contents = await fs.readFile(file,"utf8");
     console.log(contents);
  })); 
}

Could you please explain why does for ... of ... work?

2021-02-25 • Patcholi

Ok i know why... Using Babel will transform async/await to generator function and using forEach means that each iteration has an individual generator function, which has nothing to do with the others. so they will be executed independently and has no context of next() with others. Actually, a simple for() loop also works because the iterations are also in one single generator function.

2021-02-26 • GassyCassy

In short, because it was designed to work :-) await suspends the current function evaluation, including all control structures. Yes, it is quite similar to generators in that regard (which is why they are used to polyfill async/await).

2021-02-27 • paulinho33 Expert User

So files.map(async (file) => ... is equivalent to files.map((file) => new Promise((rej, res) => { ...?

2021-03-01 • Patcholi

Not really, an async function is quite different from a Promise executor callback, but yes the map callback returns a promise in both cases.

2021-03-02 • paulinho33 Expert User

When you come to learn about JS promises, but instead use half an hour translating latin ;)

2021-03-03 • paulinho33 Expert User

Best  Best Answer
const { forEach } = require("p-iteration");
const fs = require("fs-promise");

async function printFiles() {
  const files = await getFilePaths();

  await forEach(files, async (file) => {
    const contents = await fs.readFile(file, "utf8");
    console.log(contents);
  });
}

Worked for me! Thanks a lot!

2021-03-04 • LuceB3

You're welcome :D

2021-03-05 • eliseu_goat