Query | JavaScript:
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.
Using async-await with a forEach loop- Answer #1:
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)
}));
}
Using async-await with a forEach loop- Answer #2:
With ES2018, you are able to greatly simplify all of the above answers to:
async function printFiles () {
const files = await getFilePaths()
for await (const contents of files.map(file => fs.readFile(file, 'utf8'))) {
console.log(contents)
}
}
Using async-await with a forEach loop- Answer #3:
Instead of Promise.all
in conjunction with Array.prototype.map
(which does not guarantee the order in which the Promise
s are resolved), I use Array.prototype.reduce
, starting with a resolved Promise
:
async function printFiles () {
const files = await getFilePaths();
await files.reduce(async (promise, file) => {
// This line will wait for the last async function to finish.
// The first iteration uses an already resolved Promise
// so, it will immediately continue.
await promise;
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}, Promise.resolve());
}
Using async-await with a forEach loop- Answer #4:
The p-iteration module on npm implements the Array iteration methods so they can be used in a very straightforward way with async/await.
An example with your case:
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);
});
})();
Using async-await with a forEach loop- Answer #5:
Picture worth 1000 words – For Sequential Approach Only
Background : I was in similar situation last night. I used async function as foreach argument. The result was un-predictable. When I did testing for my code 3 times, it ran without issues 2 times and failed 1 time. (something weird)
Finally I got my head around & did some scratch pad testing.
Scenario 1 – How un-sequential it can get with async in foreach

const getPromise = (time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`Promise resolved for ${time}s`)
}, time)
})
}
const main = async () => {
const myPromiseArray = [getPromise(1000), getPromise(500), getPromise(3000)]
console.log('Before For Each Loop')
myPromiseArray.forEach(async (element, index) => {
let result = await element;
console.log(result);
})
console.log('After For Each Loop')
}
main();
Scenario 2 – Using for - of
loop as suggested above

const getPromise = (time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`Promise resolved for ${time}s`)
}, time)
})
}
const main = async () => {
const myPromiseArray = [getPromise(1000), getPromise(500), getPromise(3000)]
console.log('Before For Each Loop')
// AVOID USING THIS
// myPromiseArray.forEach(async (element, index) => {
// let result = await element;
// console.log(result);
// })
// This works well
for (const element of myPromiseArray) {
let result = await element;
console.log(result)
}
console.log('After For Each Loop')
}
main();
If you are a little old school like me, you could simply use the classic for loop, that works too 🙂
const getPromise = (time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`Promise resolved for ${time}s`)
}, time)
})
}
const main = async () => {
const myPromiseArray = [getPromise(1000), getPromise(500), getPromise(3000)]
console.log('Before For Each Loop')
// AVOID USING THIS
// myPromiseArray.forEach(async (element, index) => {
// let result = await element;
// console.log(result);
// })
// This works well too - the classic for loop :)
for (let i = 0; i < myPromiseArray.length; i++) {
const result = await myPromiseArray[i];
console.log(result);
}
console.log('After For Each Loop')
}
main();
Hope this helped you.
Follow Programming Articles for more!