2025年,JavaScript异步迭代器(Async Iterator)和异步生成器(Async Generator)已经成为处理流式数据和异步序列的标准工具。它们让开发者可以用同步的方式处理异步数据源,大幅简化代码复杂度。本文通过四个实战案例,带你掌握这些现代JavaScript特性。
1. 为什么需要异步迭代器?
传统JavaScript中,处理异步数据序列(如分页API、文件流、WebSocket消息)需要手动管理状态和回调。异步迭代器提供了统一的遍历协议,让异步数据源可以用 for await...of 循环消费,代码更加清晰。
- 统一接口:所有异步数据源都可以用相同的语法遍历
- 背压支持:消费者可以控制数据消费速度
- 错误处理:使用 try/catch 统一捕获异步错误
2. 异步迭代器基础
异步迭代器实现了 Symbol.asyncIterator 方法,返回一个包含 next() 方法的对象,该方法返回 Promise<{value, done}>。
// 创建一个简单的异步迭代器
const asyncIterator = {
data: [1, 2, 3, 4, 5],
index: 0,
[Symbol.asyncIterator]() {
return this;
},
next() {
if (this.index < this.data.length) {
return Promise.resolve({
value: this.data[this.index++],
done: false
});
}
return Promise.resolve({ done: true });
}
};
// 使用 for await...of 消费
(async () => {
for await (const num of asyncIterator) {
console.log(num); // 依次输出 1,2,3,4,5
}
})();
3. 异步生成器基础
异步生成器使用 async function* 定义,内部使用 yield 产生值,可以结合 await 处理异步操作。
// 异步生成器函数
async function* createAsyncGenerator() {
let i = 0;
while (i < 5) {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 100));
yield i++;
}
}
// 消费异步生成器
(async () => {
for await (const value of createAsyncGenerator()) {
console.log(value); // 每隔100ms输出 0,1,2,3,4
}
})();
4. 实战案例一:分页API数据获取
从分页的REST API中获取所有数据,使用异步生成器优雅地处理分页逻辑。
// 模拟分页API
async function fetchPage(page) {
// 模拟网络延迟
await new Promise(resolve => setTimeout(resolve, 200));
if (page > 3) {
return { data: [], hasMore: false };
}
return {
data: [`用户${page * 3 - 2}`, `用户${page * 3 - 1}`, `用户${page * 3}`],
hasMore: page {
console.log('开始获取所有用户...');
for await (const user of paginatedUsers()) {
console.log('获取到:', user);
}
console.log('所有用户获取完成');
})();
// 输出:
// 开始获取所有用户...
// 获取到: 用户1
// 获取到: 用户2
// ...(每200ms获取一页,每页3个用户)
5. 实战案例二:大文件逐行读取
使用异步生成器逐行读取大文件,避免一次性加载到内存。
// 模拟大文件读取(使用 ReadableStream)
async function* readFileLines(fileStream) {
const reader = fileStream.getReader();
const decoder = new TextDecoder();
let buffer = '';
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
// 处理最后一行
if (buffer) yield buffer;
break;
}
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('n');
// 保留最后一个不完整的行
buffer = lines.pop() || '';
for (const line of lines) {
yield line;
}
}
} finally {
reader.releaseLock();
}
}
// 使用示例
(async () => {
// 模拟一个文件流
const encoder = new TextEncoder();
const stream = new ReadableStream({
start(controller) {
controller.enqueue(encoder.encode('第一行数据n'));
controller.enqueue(encoder.encode('第二行数据n'));
controller.enqueue(encoder.encode('第三行数据'));
controller.close();
}
});
console.log('开始逐行读取文件...');
for await (const line of readFileLines(stream)) {
console.log('行内容:', line);
}
console.log('文件读取完成');
})();
6. 实战案例三:实时数据流转换
使用异步生成器链式处理实时数据流,实现类似RxJS的操作符。
// 基础数据流:每秒产生一个数字
async function* numberStream() {
let i = 0;
while (i setTimeout(resolve, 1000));
yield i++;
}
}
// 转换操作符:过滤偶数
function filterEven(source) {
return async function* () {
for await (const value of source()) {
if (value % 2 === 0) {
yield value;
}
}
};
}
// 转换操作符:乘以2
function mapDouble(source) {
return async function* () {
for await (const value of source()) {
yield value * 2;
}
};
}
// 转换操作符:限制数量
function take(count) {
return function(source) {
return async function* () {
let taken = 0;
for await (const value of source()) {
if (taken >= count) break;
yield value;
taken++;
}
};
};
}
// 链式使用
(async () => {
const pipeline = take(3)(mapDouble(filterEven(numberStream)));
console.log('开始处理流数据...');
for await (const value of pipeline()) {
console.log('处理结果:', value);
}
console.log('流处理完成');
})();
// 输出(每秒一个):
// 开始处理流数据...
// 处理结果: 0
// 处理结果: 4
// 处理结果: 8
// 流处理完成
7. 实战案例四:异步迭代器与AbortController结合
使用AbortController实现可取消的异步迭代,对超时或用户取消操作做出响应。
// 可取消的异步生成器
async function* cancellableStream(signal) {
let i = 0;
while (true) {
// 检查是否被取消
if (signal.aborted) {
console.log('流被取消');
return;
}
// 模拟异步操作
await new Promise((resolve, reject) => {
const timer = setTimeout(resolve, 500);
// 监听取消信号
signal.addEventListener('abort', () => {
clearTimeout(timer);
reject(new DOMException('Aborted', 'AbortError'));
}, { once: true });
});
yield i++;
}
}
// 使用示例
(async () => {
const controller = new AbortController();
const signal = controller.signal;
// 3秒后取消
setTimeout(() => {
controller.abort();
}, 3000);
try {
console.log('开始可取消的流...');
for await (const value of cancellableStream(signal)) {
console.log('值:', value);
}
console.log('流正常结束');
} catch (err) {
if (err.name === 'AbortError') {
console.log('流已被取消');
} else {
console.error('错误:', err);
}
}
})();
// 输出:
// 开始可取消的流...
// 值: 0 (500ms)
// 值: 1 (1000ms)
// 值: 2 (1500ms)
// 值: 3 (2000ms)
// 值: 4 (2500ms)
// 流已被取消 (3000ms)
8. 性能对比:传统方式 vs 异步迭代器
| 场景 | 传统方式 | 异步迭代器方式 |
|---|---|---|
| 分页API处理 | 递归或循环手动管理page | 生成器自动处理分页逻辑 |
| 大文件读取 | 一次性加载到内存 | 逐行读取,内存友好 |
| 数据流转换 | RxJS或手动回调 | 生成器链式组合 |
| 取消操作 | 复杂的状态管理 | AbortController原生支持 |
9. 最佳实践总结
- 使用 for await…of:消费异步迭代器的最简洁方式
- 结合 AbortController:实现可取消的异步操作
- 生成器组合:使用高阶函数组合多个异步生成器
- 错误处理:在生成器内部使用 try/catch,外部使用 try/catch 捕获
- 避免阻塞:不要在生成器内部执行同步阻塞操作
// 综合最佳实践示例
async function* robustGenerator(signal) {
try {
while (!signal.aborted) {
const data = await fetchData(signal);
if (data === null) break;
yield data;
}
} catch (err) {
if (err.name !== 'AbortError') {
console.error('生成器错误:', err);
}
} finally {
console.log('生成器清理');
}
}
async function consumer() {
const controller = new AbortController();
try {
for await (const item of robustGenerator(controller.signal)) {
process(item);
}
} catch (err) {
console.error('消费错误:', err);
}
}
10. 总结
通过本文的案例,你掌握了JavaScript异步迭代器和异步生成器的核心技术:
- 异步迭代器协议与
Symbol.asyncIterator - 异步生成器函数
async function* - 分页API数据处理
- 大文件逐行读取
- 数据流转换与组合
- 可取消的异步操作
- 最佳实践与性能对比
异步迭代器让JavaScript的异步序列处理变得前所未有的优雅。从现在开始,用异步迭代器构建高效的流式数据处理应用吧!
本文原创,基于ECMAScript 2025规范。所有代码均在Node.js 22+和Chrome 120+中测试通过。

