主题
异步迭代
异步迭代用于处理“值会陆续到达”的场景。和普通迭代不同,它不是立刻拿到下一个值,而是先等待一个 Promise 完成,再继续取值。
什么场景会用到
更常见的情况是:
- 分批读取远程数据
- 按流逐段消费内容
- 持续接收异步事件
- 边等待边处理,而不是一次性拿完整结果
先分清几个概念
- iterable:通过
Symbol.iterator提供同步迭代 - async iterable:通过
Symbol.asyncIterator提供异步迭代 - iterator:每次调用
next()返回一个结果对象 - async iterator:每次调用
next()返回一个 Promise
可以先记成:普通迭代是“直接给结果”,异步迭代是“先等,再给结果”。
异步可迭代对象
如果一个对象实现了 Symbol.asyncIterator,它就可以被 for await...of 消费:
js
const range = {
from: 1,
to: 3,
[Symbol.asyncIterator]() {
let current = this.from
const end = this.to
return {
async next() {
await new Promise((resolve) => setTimeout(resolve, 300))
if (current <= end) {
return { value: current++, done: false }
}
return { value: undefined, done: true }
},
}
},
}这里的关键区别在于:next() 不再直接返回 { value, done },而是返回一个 Promise。
for await...of
消费异步可迭代对象时,通常使用 for await...of:
js
;(async () => {
for await (const value of range) {
console.log(value) // 1 2 3
}
})()它的执行过程可以粗略理解成:
- 调用
obj[Symbol.asyncIterator]() - 拿到异步迭代器
- 反复
await iterator.next() - 当
done === true时结束
手动调用 next()
异步迭代器也可以手动消费:
js
const iterator = range[Symbol.asyncIterator]()
iterator.next().then(console.log)
iterator.next().then(console.log)只是大多数场景下,for await...of 更直接。
异步生成器
如果要按顺序异步地产出一批值,通常可以直接使用异步生成器:
js
async function* generateSequence(start, end) {
for (let i = start; i <= end; i++) {
await new Promise((resolve) => setTimeout(resolve, 300))
yield i
}
}它相当于把“异步逻辑”和“生成器语法”结合在一起。
异步生成器也能直接遍历
js
;(async () => {
for await (const value of generateSequence(1, 3)) {
console.log(value) // 1 2 3
}
})()这通常比手写 Symbol.asyncIterator + next() 更简洁。
和普通生成器的区别
普通生成器:
js
function* gen() {
yield 1
yield 2
}异步生成器:
js
async function* gen() {
yield 1
yield 2
}主要差别不是 yield 本身,而是消费方式:
- 普通生成器配合
for...of - 异步生成器配合
for await...of
和 Promise 数组的区别
这也是一个常见混淆点。
js
const list = [Promise.resolve(1), Promise.resolve(2)]这只是“数组里放了 Promise”,不等于异步可迭代对象本身就是分段产生值。
异步迭代更强调:
- 值按顺序陆续到来
- 每次取下一个值都可能要等待
- 不一定一开始就拥有完整集合
什么时候适合用异步迭代
更适合:
- 数据天然按流到达
- 需要逐条消费异步结果
- 不希望一次性把所有结果放进内存
- 处理过程需要边读边做
如果只是 await fetch() 后拿一个完整数组,再普通遍历,通常没必要上异步迭代。
使用建议
- 需要“逐步异步地产出值”时,再考虑异步迭代。
- 优先使用
async function*,通常比手写异步迭代器更清晰。 - 分清“Promise 数组”和“异步可迭代对象”不是同一回事。
- 看到
for await...of时,先确认对象是否实现了Symbol.asyncIterator。
