forEach 循环里能用 async/await 吗
forEach 不支持 async/await——回调返回的 Promise 不会被等待,循环会瞬间跑完。串行处理用 for...of 循环,并行处理用 Promise.all 配合 map。
不能按预期工作。 forEach 不会等待回调里的 Promise:异步任务全部瞬间发出,循环"立即结束",函数不会等它们完成。正确做法:串行用 for...of,并行用 Promise.all + map。
错误示范
// 不会按顺序等待!三条日志几乎同时打出,done 最先输出
[1, 2, 3].forEach(async (n) => {
await delay(n * 1000);
console.log(n);
});
console.log('done'); // 最先执行
原因:forEach 只是同步地调用回调并丢弃返回值,await 的 Promise 没人接。
方案一:for...of 串行
async function process(list) {
for (const item of list) {
const result = await doAsync(item); // 一个完成再做下一个
console.log(result);
}
}
适合后一个任务依赖前一个结果、或需要控制请求速率的场景。
方案二:Promise.all 并行
async function processParallel(list) {
const results = await Promise.all(list.map(item => doAsync(item)));
console.log(results); // 全部完成,结果顺序与输入一致
}
所有任务同时出发,总耗时约等于最慢的一个。
其他变体
Promise.allSettled:并行但允许部分失败,结果带 status 标记;for await...of:遍历异步可迭代对象(如流、分页接口);- 限流并行:用 p-limit 等库控制并发数。
常见问题(FAQ)
Q:for 和 for...in 可以吗?
普通 for 可以(等价 for...of 的 await 行为)。for...in 遍历的是键名(字符串),遍历数组请用 for...of。
Q:filter/reduce 里的异步怎么处理?
filter 无法直接 await(返回值是 Promise 全为真值)。先 map 出结果数组再过滤:const flags = await Promise.all(list.map(check)); list.filter((_, i) => flags[i])。
Q:想让 forEach 至少触发异步任务可以吗?
可以触发,但你失去了完成时机和错误处理。除非明确的"发后即忘",否则不要用。