JavaScript 中的迭代协议与异步迭代器的区别与实现
字数 1338 2025-12-09 18:45:45
JavaScript 中的迭代协议与异步迭代器的区别与实现
描述
在 JavaScript 中,迭代协议(Iteration Protocols)包含可迭代协议和迭代器协议,它们是 ES6 引入的标准化数据遍历机制。而异步迭代器是 ES2018 引入的用于遍历异步数据源的协议。理解两者的区别和实现方式对于处理同步和异步数据流至关重要。
知识点详解
步骤 1:同步迭代协议回顾
首先回顾同步迭代协议的两个核心部分:
-
可迭代协议(Iterable Protocol)
- 要求对象实现
@@iterator方法,即Symbol.iterator属性 - 该方法返回一个迭代器对象
- 要求对象实现
-
迭代器协议(Iterator Protocol)
- 迭代器对象必须实现
next()方法 next()返回包含value和done属性的对象
- 迭代器对象必须实现
代码示例:
// 自定义可迭代对象
const myIterable = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
}
return { value: undefined, done: true };
}
};
}
};
// 使用 for...of 遍历
for (const item of myIterable) {
console.log(item); // 1, 2, 3
}
步骤 2:异步迭代协议的引入
异步迭代器解决了遍历异步数据源的问题,比如从网络分页获取数据、读取文件流等。
异步迭代协议包含:
-
异步可迭代协议(Async Iterable Protocol)
- 实现
Symbol.asyncIterator属性 - 返回一个异步迭代器对象
- 实现
-
异步迭代器协议(Async Iterator Protocol)
- 迭代器的
next()方法返回 Promise - Promise 解析后返回
{ value, done }对象
- 迭代器的
代码示例:
// 模拟异步数据源
const createAsyncIterable = (data, delay) => ({
[Symbol.asyncIterator]() {
let index = 0;
return {
next: () => {
return new Promise(resolve => {
setTimeout(() => {
if (index < data.length) {
resolve({ value: data[index++], done: false });
} else {
resolve({ value: undefined, done: true });
}
}, delay);
});
}
};
}
});
// 使用 for await...of
async function processAsyncData() {
const asyncIterable = createAsyncIterable([1, 2, 3], 100);
for await (const item of asyncIterable) {
console.log(item); // 1, 2, 3 (间隔100ms输出)
}
}
processAsyncData();
步骤 3:核心区别对比
详细对比同步和异步迭代器的关键差异:
| 特性 | 同步迭代器 | 异步迭代器 |
|---|---|---|
| 符号属性 | Symbol.iterator |
Symbol.asyncIterator |
| next()返回值 | { value, done } 对象 |
Promise<{ value, done }> |
| 遍历语句 | for...of |
for await...of |
| 可迭代对象 | 数组、Map、Set等 | 异步生成器、Node.js流等 |
| 错误处理 | 同步try-catch | 异步try-catch或.catch() |
步骤 4:异步生成器实现
异步生成器是创建异步迭代器的简洁语法:
async function* asyncGenerator() {
const urls = ['/api/data1', '/api/data2'];
for (const url of urls) {
// 模拟异步请求
const response = await fetch(url);
const data = await response.json();
yield data;
}
}
// 使用异步生成器
async function fetchAllData() {
for await (const data of asyncGenerator()) {
console.log('Received:', data);
}
}
步骤 5:手动调用异步迭代器
理解如何手动控制异步迭代过程:
async function manualIteration() {
const asyncIterable = {
async *[Symbol.asyncIterator]() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
yield await Promise.resolve(3);
}
};
const iterator = asyncIterable[Symbol.asyncIterator]();
// 手动遍历
let result = await iterator.next();
while (!result.done) {
console.log(result.value);
result = await iterator.next();
}
}
步骤 6:实际应用场景
异步迭代器在实际开发中的常见应用:
- 分页API数据获取
async function* paginatedData(fetchPage) {
let page = 1;
let hasMore = true;
while (hasMore) {
const { data, hasMore: more } = await fetchPage(page);
for (const item of data) {
yield item;
}
hasMore = more;
page++;
}
}
- Node.js 流处理
async function processStream(readableStream) {
for await (const chunk of readableStream) {
console.log('Processing chunk:', chunk.length);
}
}
- WebSocket 消息处理
async function* messageIterator(socket) {
while (socket.readyState === WebSocket.OPEN) {
const message = await new Promise(resolve => {
socket.addEventListener('message', resolve, { once: true });
});
yield message.data;
}
}
步骤 7:错误处理机制
正确处理异步迭代中的错误:
async function* errorHandlingGenerator() {
try {
yield await Promise.resolve('Success 1');
throw new Error('Something went wrong');
yield await Promise.resolve('Success 2'); // 不会执行
} catch (error) {
console.error('Generator caught:', error.message);
yield 'Recovered data';
} finally {
console.log('Cleaning up...');
}
}
async function handleErrors() {
const iterator = errorHandlingGenerator();
try {
console.log(await iterator.next()); // { value: 'Success 1', done: false }
console.log(await iterator.next()); // Generator caught: Something went wrong
// { value: 'Recovered data', done: false }
console.log(await iterator.next()); // Cleaning up...
// { value: undefined, done: true }
} catch (error) {
console.error('Iterator error:', error);
}
}
步骤 8:性能考虑与最佳实践
- 并发控制:避免同时发起太多异步请求
- 资源清理:确保在提前退出时释放资源
- 取消机制:实现迭代的中途取消
async function* withCancellation(dataSource, signal) {
for (const item of dataSource) {
// 检查是否被取消
if (signal.aborted) {
throw new DOMException('Aborted', 'AbortError');
}
const result = await fetchData(item);
yield result;
}
}
总结
同步迭代器和异步迭代器提供了统一的遍历接口,但工作方式有本质区别。同步迭代器适用于内存中的数据集合,而异步迭代器专为处理异步数据流设计。理解两者的区别和适用场景,能够帮助你选择正确的工具来处理不同的数据遍历需求,特别是在现代Web应用中处理流式数据、分页API和实时数据流时,异步迭代器显示出其独特价值。