循环方法与 async/await 的兼容性

Posted by CodingWithAlice on March 22, 2025

循环方法与 async/await 的兼容性

场景:forEach 遍历过程中 await 接口返回失效

  • 总结:

    1、不能和 async/await 一起使用的循环方法:forEach、map、filter、reduce 2、能够和 async/await 一起使用的循环方法:for循环、for-of、for-in

一、好用的 异步并行执行 的写法

并行:for-of/map 中同时启动所有异步任务 + Promise.all
async function foo(things){
    const results = [];
    for(const thing of things) {
        results.push(bar(thing)) // 立即执行并返回 Promise,所有异步任务同时启动
    }
    return baz(await Promise.all(results)) // 等待所有任务完成,并使用 baz 处理结果
}
// 【更优】整个函数都可以更简洁
async function foo(things){
    const results = await Promise.allSettled(things.map(it => bar(it)))
    return baz(results)
}
串行:在 for-of 中 await
// 【较差写法】- 串行
async function foo(things){
    const results = [];
    for(const thing of things) {
        results.push(await bar(thing)) // 每个任务串行执行,效率低
    }
    return baz(results)
}

二、为什么在一些循环中无法使用

1、forEach

forEach 不等待异步回调,会立即 同步遍历所有元素,不会等待回调函数中的异步操作完成

  • 即使回调函数标记为 async,forEach 也不会等待,会继续下一次迭代
// 不会按预期工作
array.forEach(async (it) => {
  await someAsyncOperation(it); // 不会被等待
});
2、map

map 会返回包含 promise 的数组,但本身不会等待任何 promise 解决

// 不会按预期工作
const results = await array.map(async (item) => {
  return await someAsyncOperation(item); // 返回的是 Promise ,而不是解决后的值数组
});
3、filter

filter 同步执行,无法等待异步条件判断

  • async 函数返回 Promise,而 Promise 是 truthy 值,所以所有元素通常都会被保留
// 不会按预期工作
const filtered = array.filter(async (item) => {
  return await someAsyncCheck(item); // 总是返回 Promise(truthy)
});
4、reduce

reduce 累加器无法正确处理异步操作,会同步执行所有迭代

  • 会导致累加器接收 Promise 而不是解决后的值
// 不会按预期工作
const result = await array.reduce(async (acc, item) => {
  const resolvedAcc = await acc;
  return resolvedAcc + await someAsyncOperation(item);
}, Promise.resolve(0)); // 虽然能工作,但代码复杂且容易出错