在forEach循环中使用async/await的问题与解决方案

在forEach循环中使用async/await的问题与解决方案

技术背景

在JavaScript的异步编程中,async/await 语法极大地简化了异步操作的处理。然而,当在 forEach 循环中使用 async/await 时,会出现一些不符合预期的情况。forEach 方法本身是同步的,它不会等待每个异步操作完成就会继续执行下一次迭代,这可能导致异步操作的结果处理不符合预期。

实现步骤

顺序读取文件

如果需要按顺序读取文件,不能使用 forEach 循环,而应使用现代的 for...of 循环:

1
2
3
4
5
6
7
8
async function printFiles () {
const files = await getFilePaths();

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

并行读取文件

若要并行读取文件,可使用 map 方法结合 Promise.all

1
2
3
4
5
6
7
8
async function printFiles () {
const files = await getFilePaths();

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

使用 for await...of 简化操作(ES2018)

1
2
3
4
5
6
7
async function printFiles () {
const files = await getFilePaths();

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

核心代码

测试 forEach 循环中异步操作的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const array = [1, 2, 3];

const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const delayedSquare = (num) => sleep(100).then(() => num * num);

const testForEach = (numbersArray) => {
const store = [];
numbersArray.forEach(async (num) => {
const squaredNum = await delayedSquare(num);
console.log(squaredNum);
store.push(squaredNum);
});
console.log("store", store);
};
testForEach(array);

解决 forEach 问题的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 使用 for...of 循环
for (const file of files) {
const contents = await fs.readFile(file, 'utf8');
}

// 使用 reduce 方法
async function printFiles () {
const files = await getFilePaths();

await files.reduce(async (promise, file) => {
await promise;
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}, Promise.resolve());
}

最佳实践

  • 当需要顺序执行异步操作时,使用 for...of 或传统的 for 循环。
  • 当需要并行执行异步操作时,使用 map 方法结合 Promise.all
  • 避免在 forEach 循环中使用 async/await,因为 forEach 不会等待异步操作完成。

常见问题

问题1:forEach 循环中的异步操作不按预期顺序执行

forEach 是同步方法,它不会等待每个异步操作完成就会继续执行下一次迭代。解决方法是使用 for...ofreduce 方法。

问题2:forEach 循环后无法获取异步操作的结果

由于 forEach 不会等待异步操作完成,循环结束时异步操作可能还未完成。解决方法是使用 Promise.all 等待所有异步操作完成。

问题3:并行读取文件时结果顺序不一致

使用 Promise.all 并行读取文件时,结果顺序可能与文件数组顺序不一致。可以使用 reduce 方法确保按顺序读取文件。


在forEach循环中使用async/await的问题与解决方案
https://119291.xyz/posts/2025-05-09.async-await-in-foreach-loop/
作者
ww
发布于
2025年5月9日
许可协议