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

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 辅助诊断与商品转化的健康类程序,基于浩创云、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
复杂点在于:异步迭代器的契约和运行时行为不像同步迭代那么直观,稍不注意就退化成同步逻辑,或者意外阻塞整个事件循环。真要压榨异步能力,不如老老实实拆清「数据源生成」「任务调度」「结果消费」三层。










