JavaScript 生成器与迭代器协议的紧密结合
- 生成器对象满足迭代器协议
- 迭代器协议规定,一个迭代器对象必须有一个
next()
方法。当调用 next()
方法时,它返回一个包含 value
和 done
属性的对象。
- 生成器函数通过
yield
关键字暂停和恢复执行,生成器对象(由生成器函数调用返回)自然地满足迭代器协议。每次调用生成器对象的 next()
方法,生成器函数执行到下一个 yield
语句,yield
后面的值作为 next()
方法返回对象的 value
,当生成器函数执行完毕,done
为 true
。
- 例如:
function* myGenerator() {
yield 1;
yield 2;
return 3;
}
const gen = myGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: true }
- 自定义迭代器使对象能被生成器函数消费
- 要使一个对象可迭代,需要为其定义一个
Symbol.iterator
方法,该方法返回一个迭代器对象(满足迭代器协议)。
- 生成器函数可以消费这样的可迭代对象,通过
for...of
循环等方式。for...of
循环会自动调用可迭代对象的 Symbol.iterator
方法获取迭代器,然后不断调用迭代器的 next()
方法。
- 例如,实现一个自定义的可迭代对象
MyIterable
:
const MyIterable = {
data: [1, 2, 3],
[Symbol.iterator]: function() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};
function* consumeIterable() {
for (let value of MyIterable) {
yield value * 2;
}
}
const consumer = consumeIterable();
console.log(consumer.next()); // { value: 2, done: false }
console.log(consumer.next()); // { value: 4, done: false }
console.log(consumer.next()); // { value: 6, done: false }
console.log(consumer.next()); // { value: undefined, done: true }
交互机制在复杂数据处理场景中的应用和优化策略
- 应用
- 惰性求值:生成器和迭代器结合实现惰性求值,在处理大数据集时,不会一次性加载所有数据到内存,而是按需生成和处理数据。例如,处理一个非常大的文件,逐行读取并处理,而不是将整个文件读入内存。
- 异步操作控制:在异步场景中,可以利用生成器和迭代器实现异步操作的顺序控制。通过
yield
暂停生成器,等待异步操作完成后再恢复执行。比如,使用 yield
暂停等待 Promise
解决,从而实现顺序执行多个异步任务。
- 优化策略
- 减少中间数据存储:在生成器处理数据过程中,尽量避免创建大量中间数据结构。例如,在对数据进行多步转换时,可以通过链式调用生成器,直接从一个生成器输出流入下一个生成器输入,减少中间数组等数据结构的创建。
- 缓存中间结果:对于一些重复计算的部分,可以缓存中间结果。如果生成器在不同阶段会多次使用某个计算结果,可以在第一次计算后将其缓存起来,后续直接使用缓存值,提高效率。