如何用 for await...of 遍历包含异步操作的数组列表

夏丽君_1677

夏丽君_1677

2026-05-11

1008人浏览

原创

for await...of不能直接遍历普通数组,因其仅支持实现[symbol.asynciterator]的异步迭代器;普通数组需用for...of+await顺序执行,或promise.all并发处理。

如何用 for await...of 遍历包含异步操作的数组列表

for await...of 不能直接遍历普通数组

很多人写 for await...of 时,下意识把一个普通数组(比如 const list = [fetch('/a'), fetch('/b')])直接丢进去,结果发现代码同步执行、没等 Promise 完成就结束了——这是因为 for await...of 只对「可迭代的异步迭代器」有效,而普通数组的迭代器返回的是值,不是 Promise,更不是 AsyncIterator。

常见错误现象:TypeError: list is not async iterable 不会报(因为数组本身是可迭代的),但实际行为像同步 for...of,await 被忽略。

  • 真正能用 for await...of 的对象:实现了 [Symbol.asyncIterator] 的对象,比如 ReadableStream、某些自定义异步生成器、或包装后的 Promise 数组
  • 普通数组 + Promise 元素 → 需先转成异步可迭代形式,不能硬套
  • 如果只是想“顺序等待每个 Promise”,更直接的做法是用 for...of + await,而非强求 for await...of

正确做法:用 for...of + await 顺序执行

绝大多数场景下,你想要的只是“一个接一个地发起请求并等它完成”,这时候最自然、最易读、最不容易出错的方式是:

const urls = ['/api/a', '/api/b', '/api/c'];
for (const url of urls) {
  const res = await fetch(url);
  const data = await res.json();
  console.log(data);
}

这个写法清晰表达了控制流意图:串行、阻塞、按序执行。它不依赖数组是否“异步可迭代”,也不需要额外转换。

AI舌诊面诊问诊医疗系统+诊疗商城系统源码
AI舌诊面诊问诊医疗系统+诊疗商城系统源码

AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。

下载
  • 性能影响:确实是串行,吞吐低,但语义明确;如需并发,请用 Promise.all,而不是强行用 for await...of
  • 错误处理直接:在循环体内 try/catch 即可捕获单个请求失败,不影响后续
  • 注意:必须在 async 函数内使用,否则 await 语法报错 SyntaxError: await is only valid in async function

真要用 for await...of?得先构造 AsyncIterator

如果你有特殊需求(比如流式处理大量数据、或与底层异步流对接),才需要手动构造异步迭代器。一个轻量封装示例如下:

function asyncMap(iterable, fn) {
  return {
    [Symbol.asyncIterator]() {
      const it = iterable[Symbol.iterator]();
      return {
        next() {
          const { done, value } = it.next();
          if (done) return Promise.resolve({ done, value });
          return Promise.resolve(fn(value)).then(v => ({ done, value: v }));
        }
      };
    }
  };
}

// 使用
const urls = ['/a', '/b', '/c'];
for await (const data of asyncMap(urls, u => fetch(u).then(r => r.json()))) {
  console.log(data);
}

这段代码本质是把同步迭代器桥接到异步迭代协议上。但它增加了理解成本,且容易在 next() 抛异常时漏处理错误。

  • 不要复用这个模式去“优化”简单列表请求——得不偿失
  • asyncMap 返回的对象没有 return() 或 throw() 实现,遇到 break 或异常退出时无法清理资源
  • 浏览器/Node.js 对 [Symbol.asyncIterator] 的支持已稳定,但调试时看不到原生数组的异步迭代痕迹,容易误判

并发 vs 串行:别用 for await...of 混淆概念

有人试图用 for await...of 实现“并发请求+按响应顺序处理”,这是误解。它既不自动并发,也不保证响应顺序与发起顺序一致——除非你显式控制 Promise 创建时机。

  • 并发请求应优先考虑 Promise.allSettled() 或 Promise.all(),配合 map() 创建 Promise 数组
  • for await...of 在 ReadableStream 场景中才有“边接收边处理”的真实流意义,比如 response.body 解析
  • 最容易被忽略的一点:即使你包装了异步迭代器,只要 next() 内部是 await fetch(),它仍是串行的;并发必须在 next() 外提前触发所有 Promise

复杂点在于:异步迭代器的契约和运行时行为不像同步迭代那么直观,稍不注意就退化成同步逻辑,或者意外阻塞整个事件循环。真要压榨异步能力,不如老老实实拆清「数据源生成」「任务调度」「结果消费」三层。

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

5046

5

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

2023.07.04

1249

3

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

2023.07.04

4964

6

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

2023.09.01

960

4

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

1896

5

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

2023.09.04

3545

4

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

2023.09.12

4613

6

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

2023.09.20

3020

5

java访问控制修饰符介绍
java访问控制修饰符介绍

java访问控制修饰符有四种,分别是public、protected、private、默认访问修饰符。详细介绍:1、public,public是最宽松的访问控制修饰符,被修饰的类、方法和变量可以被任何其他类访问,当一个类、方法或变量被声明为public时,它们可以在任何地方被访问,无论是同一个包中的类还是不同包中的类;2、protected修饰符等等。

2023.09.20

968

7

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程