迭代器和生成器(Iterators and Generators)
概述
迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。生成器(Generator)是 ES6 提供的一种异步编程解决方案。
迭代器(Iterator)
迭代器协议
迭代器是一个对象,它满足迭代器协议:
const iterator = {
next() {
return {
value: any, // 当前迭代的值
done: boolean // 是否迭代完成
};
}
};
基本示例
// 创建一个简单的迭代器
function createRangeIterator(start, end, step = 1) {
let current = start;
return {
next() {
if (current < end) {
const value = current;
current += step;
return { value, done: false };
}
return { done: true };
}
};
}
const iterator = createRangeIterator(1, 5);
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { done: true }
可迭代对象(Iterable)
可迭代对象实现了 Symbol.iterator 方法:
const myIterable = {
[Symbol.iterator]() {
let step = 0;
return {
next() {
step++;
if (step <= 3) {
return { value: step * 10, done: false };
}
return { done: true };
}
};
}
};
// 使用 for...of
for (const value of myIterable) {
console.log(value); // 10, 20, 30
}
// 使用展开运算符
console.log([...myIterable]); // [10, 20, 30]
// 使用解构赋值
const [a, b, c] = myIterable;
console.log(a, b, c); // 10 20 30
内置可迭代对象
// 数组
const arr = [1, 2, 3];
for (const item of arr) {
console.log(item);
}
// 字符串
const str = 'hello';
for (const char of str) {
console.log(char);
}
// Map
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {
console.log(key, value);
}
// Set
const set = new Set([1, 2, 3]);
for (const value of set) {
console.log(value);
}
// arguments 对象
function example() {
for (const arg of arguments) {
console.log(arg);
}
}
生成器(Generator)
基本语法
// 生成器函数
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
}
const generator = generatorFunction();
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 2, done: false }
console.log(generator.next()); // { value: 3, done: false }
console.log(generator.next()); // { value: undefined, done: true }
生成器特性
惰性求值
function* infiniteSequence() {
let i = 0;
while (true) {
yield i++;
}
}
const infinite = infiniteSequence();
console.log(infinite.next().value); // 0
console.log(infinite.next().value); // 1
console.log(infinite.next().value); // 2
// 可以无限继续
可暂停和恢复
function* pausedGenerator() {
console.log('开始');
yield 1;
console.log('继续');
yield 2;
console.log('结束');
}
const gen = pausedGenerator();
console.log('调用 next()');
gen.next(); // 开始
console.log('再次调用 next()');
gen.next(); // 继续
gen.next(); // 结束
yield 表达式
function* calculator() {
const x = yield '输入第一个数';
const y = yield '输入第二个数';
yield x + y;
}
const calc = calculator();
console.log(calc.next().value); // '输入第一个数'
console.log(calc.next(10).value); // '输入第二个数'
console.log(calc.next(20).value); // 30
生成器委托
function* generator1() {
yield 1;
yield 2;
}
function* generator2() {
yield 3;
yield 4;
}
function* combined() {
yield* generator1();
yield* generator2();
}
for (const value of combined()) {
console.log(value); // 1, 2, 3, 4
}
生成器与迭代器
// 生成器自动实现迭代器协议
function* range(start, end) {
for (let i = start; i < end; i++) {
yield i;
}
}
// 可以使用 for...of
for (const num of range(1, 5)) {
console.log(num); // 1, 2, 3, 4
}
// 可以使用展开运算符
console.log([...range(1, 5)]); // [1, 2, 3, 4]
// 可以使用解构赋值
const [first, second, ...rest] = range(1, 6);
console.log(first, second, rest); // 1 2 [3, 4, 5]
实际应用示例
1. 自定义可迭代对象
class Collection {
constructor(items) {
this.items = items;
}
*[Symbol.iterator]() {
for (const item of this.items) {
yield item;
}
}
*filter(predicate) {
for (const item of this) {
if (predicate(item)) {
yield item;
}
}
}
*map(transform) {
for (const item of this) {
yield transform(item);
}
}
}
const collection = new Collection([1, 2, 3, 4, 5]);
// 使用迭代器
for (const item of collection) {
console.log(item);
}
// 使用 filter 和 map
const result = collection
.filter(x => x % 2 === 0)
.map(x => x * 2);
console.log([...result]); // [4, 8]
2. 异步迭代器
class AsyncRange {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.asyncIterator]() {
let current = this.start;
const end = this.end;
return {
async next() {
await new Promise(resolve => setTimeout(resolve, 100));
if (current < end) {
return { value: current++, done: false };
}
return { done: true };
}
};
}
}
// 使用异步迭代器
async function main() {
const range = new AsyncRange(1, 5);
for await (const value of range) {
console.log(value); // 每 100ms 输出 1, 2, 3, 4
}
}
main();
3. 生成器实现协程
function* taskRunner() {
console.log('任务 1');
yield;
console.log('任务 2');
yield;
console.log('任务 3');
}
const task = taskRunner();
task.next(); // 任务 1
task.next(); // 任务 2
task.next(); // 任务 3
4. 状态机
function* stateMachine() {
while (true) {
const action = yield;
switch (action) {
case 'INCREMENT':
console.log('递增');
break;
case 'DECREMENT':
console.log('递减');
break;
case 'RESET':
console.log('重置');
break;
}
}
}
const machine = stateMachine();
machine.next(); // 初始化
machine.next('INCREMENT'); // 递增
machine.next('DECREMENT'); // 递减
machine.next('RESET'); // 重置
5. 数据流处理
function* readLines(text) {
const lines = text.split('\n');
for (const line of lines) {
yield line.trim();
}
}
function* filterLines(lines, pattern) {
for (const line of lines) {
if (line.includes(pattern)) {
yield line;
}
}
}
function* take(lines, count) {
let taken = 0;
for (const line of lines) {
if (taken >= count) break;
yield line;
taken++;
}
}
const text = `
Apple is red
Banana is yellow
Apple is delicious
Orange is orange
Apple is sweet
`;
const lines = readLines(text);
const appleLines = filterLines(lines, 'Apple');
const firstTwo = take(appleLines, 2);
console.log([...firstTwo]);
// ['Apple is red', 'Apple is delicious']
6. 斐波那契数列
```function* fibonacci() { let a = 0; let b = 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 console.log(fib.next().value); // 1 console.log(fib.next().value); // 2 console.log(fib.next().value); // 3 console.log(fib.next().value); // 5
// 获取前 10 个斐波那契数 const first10 = []; const fib2 = fibonacci(); for (let i = 0; i < 10; i++) { first10.push(fib2.next().value); } console.log(first10); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
## 高级用法
### 1. 生成器组合
```javascript
function* map(iterable, transform) {
for (const item of iterable) {
yield transform(item);
}
}
function* filter(iterable, predicate) {
for (const item of iterable) {
if (predicate(item)) {
yield item;
}
}
}
function* take(iterable, count) {
let taken = 0;
for (const item of iterable) {
if (taken >= count) break;
yield item;
taken++;
}
}
// 组合使用
const numbers = function* () {
for (let i = 1; i <= 100; i++) {
yield i;
}
};
const result = take(
filter(
map(numbers(), x => x * 2),
x => x % 3 === 0
),
5
);
console.log([...result]); // [6, 12, 18, 24, 30]
2. 错误处理
```function* errorHandling() { try { const result = yield riskyOperation(); console.log('成功:', result); } catch (error) { console.log('捕获错误:', error.message); } }
function riskyOperation() { return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.5) { resolve('操作成功'); } else { reject(new Error('操作失败')); } }, 1000); }); }
const gen = errorHandling(); gen.next().value .then(result => gen.next(result)) .catch(error => gen.throw(error));
### 3. 异步流程控制
```javascript
function run(generatorFunction) {
const generator = generatorFunction();
function handle(result) {
if (result.done) return Promise.resolve(result.value);
return Promise.resolve(result.value)
.then(res => handle(generator.next(res)))
.catch(err => handle(generator.throw(err)));
}
return handle(generator.next());
}
// 使用
function* fetchUserData() {
try {
const user = yield fetch('/api/user').then(r => r.json());
const posts = yield fetch(`/api/posts/${user.id}`).then(r => r.json());
const comments = yield fetch(`/api/comments/${posts[0].id}`).then(r => r.json());
return { user, posts, comments };
} catch (error) {
console.error('获取数据失败:', error);
throw error;
}
}
run(fetchUserData)
.then(data => console.log(data))
.catch(error => console.error(error));
注意事项
1. 生成器只能暂停,不能中断
function* infinite() {
let i = 0;
while (true) {
yield i++;
}
}
const gen = infinite();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
// 生成器会一直运行,需要外部控制
2. 生成器的 return 方法
function* generator() {
yield 1;
yield 2;
yield 3;
}
const gen = generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.return('结束')); // { value: '结束', done: true }
console.log(gen.next()); // { value: undefined, done: true }
3. 生成器的 throw 方法
function* generator() {
try {
yield 1;
yield 2;
} catch (error) {
console.log('捕获错误:', error.message);
}
yield 3;
}
const gen = generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.throw(new Error('测试错误')));
// 捕获错误: 测试错误
// { value: 3, done: false }
最佳实践
- 使用生成器处理大量数据:惰性求值节省内存
- 使用迭代器协议:使自定义对象可迭代
- 组合生成器:使用
yield*委托 - 处理异步操作:使用生成器实现协程
// 好的做法
function* processLargeDataset(data) {
for (const item of data) {
if (item.isValid) {
yield transform(item);
}
}
}
// 处理大量数据时不会占用太多内存
const processed = processLargeDataset(largeArray);
for (const item of processed) {
// 逐个处理
}
// 避免
const processed = largeArray
.filter(item => item.isValid)
.map(transform); // 一次性创建新数组,占用内存