一、为什么需要迭代协议?
在现代 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, 5result = 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 处理,从大数据处理到机器学习管道,迭代协议提供了统一的抽象层。掌握这些特性不仅能够提升代码质量,更能帮助我们构建更高效、更易维护的应用程序。