在现代 JavaScript/TypeScript 开发中,我们经常需要处理各种集合型数据:数组、Map、Set 甚至是自定义数据结构。ES6 引入的迭代协议(Iteration Protocols)正是为了解决统一遍历机制的问题。通过迭代器模式,我们可以:
为不同的数据结构提供统一的访问接口
实现惰性计算(Lazy Evaluation)
支持现代语言特性(for...of, 扩展运算符等)
构建异步迭代流程
TypeScript 通过两个核心接口实现迭代协议:
// 可迭代接口
interface Iterable {
[Symbol.iterator](): Iterator;
}
// 迭代器接口
interface Iterator {
next(): IteratorResult;
}
interface IteratorResult {
value: T | undefined;
done: boolean;
}
让我们实现一个简单的数字范围迭代器:
class RangeIterator implements Iterator {
private current: number;
constructor(
private readonly start: number,
private readonly end: number,
private readonly step: number = 1
) {
this.current = start;
}
next(): IteratorResult {
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();
}
TypeScript 支持以下内置可迭代类型:
类型 | 迭代行为 |
---|---|
Array | 按索引顺序迭代元素 |
String | 按字符迭代 |
Map | 迭代 [key, value] 键值对 |
Set | 按插入顺序迭代元素 |
NodeList | DOM 节点集合迭代 |
arguments | 函数参数对象的迭代 |
通过 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 }
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 }
生成器可以极大简化迭代器的实现:
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
}
interface AsyncIterable {
[Symbol.asyncIterator](): AsyncIterator;
}
interface AsyncIterator {
next(): Promise>;
}
实现分页数据获取:
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(iterable: Iterable, predicate: (item: T) => boolean) {
for (const item of iterable) {
if (predicate(item)) {
yield item;
}
}
}
function* map(iterable: Iterable, 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);
}
使用 debugger
语句暂停生成器执行
function* debugGenerator() {
yield 'step 1';
debugger; // 调试器将在此暂停
yield 'step 2';
}
利用 VS Code 的调试配置:
{
"type": "node",
"request": "launch",
"name": "Debug Generator",
"skipFiles": ["/**"],
"program": "${file}",
"runtimeArgs": ["--harmony-async-iteration"]
}
提前终止迭代:
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(iterator: Iterator) {
const result = iterator.next();
return result.done ? null : result.value;
}
随着 JavaScript 语言的演进,迭代器和生成器正在成为现代 Web 开发的核心模式。从 React 的 Suspense 特性到 Node.js 的 Stream 处理,从大数据处理到机器学习管道,迭代协议提供了统一的抽象层。掌握这些特性不仅能够提升代码质量,更能帮助我们构建更高效、更易维护的应用程序。