<p>Generator 本身非异步,但通过 async function* 可自然构建符合 AsyncIterator 协议的异步数据流,支持惰性拉取、背压、错误传播与终止控制,适用于分页、流式解码等场景。</p>

Generator 本身不是异步的,但它能自然地与异步迭代器(AsyncIterator)协作,构成可暂停、可背压、按需消费的类 Streams API 数据流。关键不在于“替代”Streams,而在于用更底层、更可控的方式模拟其核心能力:惰性拉取、逐块处理、错误传播和终止控制。
Generator 构造异步可迭代对象
要让 Generator 支持 for await...of,它必须返回一个符合异步迭代器协议的对象——即拥有 [Symbol.asyncIterator]() 方法,且该方法返回的对象有 next() 方法,返回 Promise。
最直接的方式是用 async function*(异步生成器函数),它自动返回 AsyncIterator:
- 每个
yield后面可以是 Promise、普通值或 async 迭代器本身 -
yield的值会包裹进{ value, done },并通过 Promise resolve 返回 - 内部可 await 异步操作,外部用
for await...of拉取
例如,模拟一个分页 API 流:
立即学习“Java免费学习笔记(深入)”;
async function* fetchPages(url, max = 5) {
for (let page = 1; page <= max; page++) {
const res = await fetch(`${url}?page=${page}`);
const data = await res.json();
yield data; // 每次 yield 都是一个异步步骤的产出
}
}
<p>// 使用方式完全类同 ReadableStream
for await (const pageData of fetchPages('/api/items')) {
console.log('处理一页:', pageData);
}手动实现 AsyncIterator 接口(更细粒度控制)
当需要干预暂停时机、注入中断逻辑或集成第三方流(如 WebSocket 消息、EventSource)时,可手动构造 AsyncIterator,并把 Generator 作为状态机驱动器:
- 定义一个对象,实现
[Symbol.asyncIterator]() { return this; } -
next()方法返回 Promise,并在其中调用 Generator 的next() - Generator 内部用
yield控制“何时请求下一块”,而非“何时返回数据”
这样就能实现真正的背压:消费者没调用 next(),Generator 就不会触发下一次 fetch 或解析。
与 Streams API 的关键差异与互补点
异步生成器 ≠ Streams,但能覆盖多数 Streams 场景,且更轻量、更易调试:
-
无内置背压信号:Streams 通过
readable.read()和controller.desiredSize显式反馈;Generator 依赖调用节奏,天然“拉取驱动” -
无内置 pipeTo / transform:但可用
async function* transform(iter)封装中间操作,链式组合 -
错误传播一致:
throw在异步生成器中会 reject 对应的next()Promise,for await可用try/catch捕获 - 可暂停/恢复任意位置:比如在 yield 前做条件判断、日志、节流,这是 Streams 管道做不到的
实用组合模式:Generator + Transform Stream 模拟
想实现类似 ReadableStream.pipeThrough(new TextDecoderStream()) 的效果?写一个转换型异步生成器:
async function* decodeLines(source) {
let buffer = '';
for await (const chunk of source) {
buffer += chunk;
const lines = buffer.split('\n');
buffer = lines.pop() || ''; // 留下不完整行
for (const line of lines) {
yield line;
}
}
if (buffer) yield buffer; // 最后一行
}
<p>// 使用
const response = await fetch('/log-stream');
const reader = response.body.getReader();
async function* readableToAsyncIter() {
while (true) {
const { done, value } = await reader.read();
if (done) break;
yield new TextDecoder().decode(value);
}
}
for await (const line of decodeLines(readableToAsyncIter())) {
console.log('收到日志行:', line);
}


















