一、为什么需要迭代协议?
在现代 JavaScript/TypeScript 开发中,我们经常需要处理各种集合型数据:数组、Map、Set 甚至是自定义数据结构。ES6 引入的迭代协议(Iteration Protocols)正是为了解决统一遍历机制的问题。通过迭代器模式,我们可以:
-
为不同的数据结构提供统一的访问接口
-
实现惰性计算(Lazy Evaluation)
-
支持现代语言特性(for...of, 扩展运算符等)
-
构建异步迭代流程
二、迭代器(Iterator)的核心机制
2.1 迭代协议双接口
TypeScript 通过两个核心接口实现迭代协议:
// 可迭代接口
interface Iterable<T> {
[Symbol.iterator](): Iterator<T>;
}
// 迭代器接口
interface Iterator<T> {
next(): IteratorResult<T>;
}
interface IteratorResult<T> {
value: T | undefined;
done: boolean;
}
2.2 自定义迭代器实战
让我们实现一个简单的数字范围迭代器:
class RangeIterator implements Iterator<number> {
private current: number;
constructor(
private readonly start: number,
private readonly end: number,
private readonly step: number = 1
) {
this.current = start;
}
next(): IteratorResult<number> {
if (this.current <= this.end) {
const value = this.current;
this.current += this.step;
return { value, done: false };
}
return { value: undefined, done: true };
}
}
// 使用示例
const range = new RangeIterator(1, 5);
let result = range.next();
while (!result.done) {
console.log(result.value); // 1, 2, 3, 4, 5
result = range.next();
}
2.3 内置可迭代对象
TypeScript 支持以下内置可迭代类型:
类型 | 迭代行为 |
---|---|
Array | 按索引顺序迭代元素 |
String | 按字符迭代 |
Map | 迭代 [key, value] 键值对 |
Set | 按插入顺序迭代元素 |
NodeList | DOM 节点集合迭代 |
arguments | 函数参数对象的迭代 |
三、生成器(Generator)的魔法
3.1 生成器基础语法
通过 function*
声明生成器函数:
function* simpleGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = simpleGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
3.2 生成器高级特性
双向通信
function* twoWayCommunication() {
const name = yield 'Please enter your name:';
const age = yield 'Please enter your age:';
return { name, age };
}
const gen = twoWayCommunication();
console.log(gen.next()); // { value: 'Please enter your name:', done: false }
console.log(gen.next('Alice')); // { value: 'Please enter your age:', done: false }
console.log(gen.next(30)); // { value: { name: 'Alice', age: 30 }, done: true }
异常处理
function* errorHandling() {
try {
yield 'Normal execution';
throw new Error('Generator error');
} catch (err) {
yield `Caught error: ${err.message}`;
}
}
const gen = errorHandling();
console.log(gen.next()); // { value: 'Normal execution', done: false }
console.log(gen.throw(new Error('External error'))); // { value: 'Caught error: External error', done: false }
3.3 生成器实现迭代器
生成器可以极大简化迭代器的实现:
function* rangeGenerator(start: number, end: number, step = 1) {
for (let i = start; i <= end; i += step) {
yield i;
}
}
// 使用 for...of 迭代
for (const num of rangeGenerator(1, 5)) {
console.log(num); // 1, 2, 3, 4, 5
}
四、异步迭代与生成器
4.1 异步迭代协议
interface AsyncIterable<T> {
[Symbol.asyncIterator](): AsyncIterator<T>;
}
interface AsyncIterator<T> {
next(): Promise<IteratorResult<T>>;
}
4.2 异步生成器实战
实现分页数据获取:
async function* paginatedFetcher(url: string, pageSize = 10) {
let page = 1;
let hasMore = true;
while (hasMore) {
const response = await fetch(`${url}?page=${page}&size=${pageSize}`);
const data = await response.json();
yield data.items;
hasMore = data.hasMore;
page++;
}
}
// 使用示例
(async () => {
const pageIterator = paginatedFetcher('/api/data');
for await (const items of pageIterator) {
console.log('Received items:', items);
}
})();
五、性能优化与最佳实践
-
惰性计算优势:生成器只在需要时产生值,显著降低内存消耗
function* fibonacci() { let [a, b] = [0, 1]; while (true) { yield a; [a, b] = [b, a + b]; } } // 仅计算需要的斐波那契数 const fib = fibonacci(); console.log(fib.next().value); // 0 console.log(fib.next().value); // 1
-
组合迭代器模式
function* filter<T>(iterable: Iterable<T>, predicate: (item: T) => boolean) { for (const item of iterable) { if (predicate(item)) { yield item; } } } function* map<T, U>(iterable: Iterable<T>, mapper: (item: T) => U) { for (const item of iterable) { yield mapper(item); } } // 使用组合 const numbers = [1, 2, 3, 4, 5]; const result = map(filter(numbers, n => n % 2 === 0), n => n * 2); console.log([...result]); // [4, 8]
-
内存优化对比
方法 内存占用 执行方式 传统数组处理 高 立即执行 生成器管道 低 按需执行 异步生成器 极低 事件驱动
六、在常见库中的应用
-
RxJS:Observable 与生成器的结合
import { from, Observable } from 'rxjs'; function* sensorData() { while (true) { yield Math.random() * 100; await sleep(1000); } } const observable$ = from(sensorData()); observable$.subscribe(console.log);
-
Redux-Saga:使用生成器管理副作用
import { call, put, takeEvery } from 'redux-saga/effects'; function* fetchUser(action) { try { const user = yield call(fetch, `/api/users/${action.payload}`); yield put({ type: 'USER_FETCH_SUCCEEDED', payload: user }); } catch (e) { yield put({ type: 'USER_FETCH_FAILED', message: e.message }); } } function* mySaga() { yield takeEvery('USER_FETCH_REQUESTED', fetchUser); }
七、调试技巧与常见陷阱
7.1 调试建议
-
使用
debugger
语句暂停生成器执行function* debugGenerator() { yield 'step 1'; debugger; // 调试器将在此暂停 yield 'step 2'; }
-
利用 VS Code 的调试配置:
{ "type": "node", "request": "launch", "name": "Debug Generator", "skipFiles": ["<node_internals>/**"], "program": "${file}", "runtimeArgs": ["--harmony-async-iteration"] }
7.2 常见错误处理
-
提前终止迭代:
function* numbers() { try { yield 1; yield 2; yield 3; } finally { console.log('Generator cleanup'); } } const gen = numbers(); console.log(gen.next()); // { value: 1, done: false } console.log(gen.return()); // 立即触发 finally 块
-
处理迭代器耗尽:
const gen = simpleGenerator(); gen.next(); // { value: 1, done: false } gen.next(); // { value: 2, done: false } gen.next(); // { value: 3, done: false } gen.next(); // { value: undefined, done: true } // 安全检测 function safeNext<T>(iterator: Iterator<T>) { const result = iterator.next(); return result.done ? null : result.value; }
结语:迭代模式的未来
随着 JavaScript 语言的演进,迭代器和生成器正在成为现代 Web 开发的核心模式。从 React 的 Suspense 特性到 Node.js 的 Stream 处理,从大数据处理到机器学习管道,迭代协议提供了统一的抽象层。掌握这些特性不仅能够提升代码质量,更能帮助我们构建更高效、更易维护的应用程序。