LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

你还在 for 循环里使用 await?异步循环得这样写

zhenglin
2025年11月7日 15:0 本文热度 317

1. 前言

在循环中使用 await,代码看似直观,但运行时要么悄无声息地停止,要么运行速度缓慢,这是为什么呢?

本篇聊聊 JavaScript 中的异步循环问题。


2. 踩坑 1:for 循环里用 await,效率太低

假设要逐个获取用户数据,可能会这样写:

const users = [1, 2, 3];

for (const id of users) {

  const user = await fetchUser(id);

  console.log(user);

}

代码虽然能运行,但会顺序执行——必须等 fetchUser(1) 完成,fetchUser(2) 才会开始。

若业务要求严格按顺序执行,这样写没问题;但如果请求之间相互独立,这种写法就太浪费时间了。



3. 踩坑 2:map 里直接用 await,拿到的全是 Promise

很多人会在 map() 里用 await,却未处理返回的 Promise,结果踩了坑:

const users = [1, 2, 3];

const results = users.map(async (id) => {

  const user = await fetchUser(id);

  return user;

});

console.log(results); // 输出 [Promise, Promise, Promise],而非实际用户数据

语法上没问题,但它不会等 Promise resolve。若想让请求并行执行并获取最终结果,需用 Promise.all()

const results = await Promise.all(users.map((id) => fetchUser(id)));

这样所有请求会同时发起results 中就是真正的用户数据了。



4. 踩坑 3:Promise.all 一错全错

用 Promise.all() 时,只要有一个请求失败,整个操作就会报错:

代码高亮:

const results = await Promise.all(

  users.map((id) => fetchUser(id)) // 假设 fetchUser(2) 出错

);

如果 fetchUser(2) 返回 404 或网络错误,Promise.all() 会直接 reject,即便其他请求成功,也拿不到任何结果。



5. 更安全的替代方案

5.1. 用 Promise.allSettled(),保留所有结果

使用 Promise.allSettled(),即便部分请求失败,也能拿到所有结果,之后可手动判断成功与否:

const results = await Promise.allSettled(users.map((id) => fetchUser(id)));


results.forEach((result) => {

  if (result.status === "fulfilled") {

    console.log("✅ 用户数据:", result.value);

  } else {

    console.warn("❌ 错误:", result.reason);

  }

});

5.2. 在 map 里加 try/catch,返回兜底值

也可在请求时直接捕获错误,给失败的请求返回默认值:

const results = await Promise.all(

  users.map(async (id) => {

    try {

      return await fetchUser(id);

    } catch (err) {

      console.error(`获取用户${id}失败`, err);

      return { id, name: "未知用户" }; // 兜底数据

    }

  })

);

这样还能避免 “unhandled promise rejections” 错误——在 Node.js 严格环境下,该错误可能导致程序崩溃。


6. 现代异步循环方案,按需选择

6.1. for...of + await:适合需顺序执行的场景

若下一个请求依赖上一个的结果,或需遵守 API 的频率限制,可采用此方案:


// 在 async 函数内

for (const id of users) {

  const user = await fetchUser(id);

  console.log(user);

}

// 不在 async 函数内,用立即执行函数

(async () => {

  for (const id of users) {

    const user = await fetchUser(id);

    console.log(user);

  }

})();

  • 优点:保证顺序,支持限流

  • 缺点:独立请求场景下速度慢

6.2. Promise.all + map:适合追求速度的场景

请求间相互独立且可同时执行时,此方案效率最高:

代码高亮:

const usersData = await Promise.all(users.map((id) => fetchUser(id)));

6.3. 限流并行:用 p-limit 控制并发数

若需兼顾速度与 API 限制,可借助 p-limit 等工具控制同时发起的请求数量:

import pLimit from "p-limit";

const limit = pLimit(2); // 每次同时发起 2 个请求

const limitedFetches = users.map((id) => limit(() => fetchUser(id)));

const results = await Promise.all(limitedFetches);

  • 优点:平衡并发和控制,避免压垮外部服务

  • 缺点:需额外引入依赖


7. 注意:千万别在 forEach() 里用 await

这是个高频陷阱:

users.forEach(async (id) => {

  const user = await fetchUser(id);

  console.log(user); // ❌ 不会等待执行完成

});

forEach() 不会等待异步回调,请求会在后台乱序执行,可能导致代码逻辑出错、错误被遗漏。

替代方案:

  • 顺序执行:用 for...of + await

  • 并行执行:用 Promise.all() + map()


8. 总结:按需选择

JavaScript 异步能力很强,但循环里用 await 要“按需选择”,核心原则如下:

 

参考文章:原文链接


该文章在 2025/11/7 15:00:32 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved