21.2 什么是生成器?
生成器就是可以暂停( pause )和唤醒( resume )的函数(考虑协同多任务处理和协同程序),这使得大量应用变为可能。
作为第一个例子,考虑下面名为 genFunc
的生成器函数:
function* genFunc() {
console.log('First');
yield; // (A)
console.log('Second'); // (B)
}
genFunc
在两个方面和普通的函数声明不一样:
- 以“关键字”
function*
开始。 - 在函数中的 yield 处暂停。
调用 genFunc
并不会执行它。相反,它返回一个所谓的生成器对象让我们能够控制 genFunc
的执行:
> let genObj = genFunc();
genFunc()
初始的时候在它的函数体开始处暂停。方法 genObj.next()
继续 genFunc
的执行,直到下一个 yield
:
> genObj.next()
First
{ value: undefined, done: false }
正如你在最后一行看到的, genObj.next()
也返回一个对象,现在先忽略它。一旦把生成器当做迭代器的时候,这就很重要了。
genFunc
现在暂停在行 A 处。如果我们再次调用 next()
,就会唤醒执行过程,行 B 得以执行:
> genObj.next()
Second
{ value: undefined, done: true }
然后,函数完成,执行流程离开函数体,继续调用 genObj.next()
不再有效果。
21.2.1 创建生成器的方式
有四种方法可以创建生成器:
- 1、通过生成器函数声明:
function* genFunc() { ··· }
let genObj = genFunc();
- 2、通过生成器函数表达式:
const genFunc = function* () { ··· };
let genObj = genFunc();
- 3、通过对象字面量中的生成器方法定义:
let obj = {
* generatorMethod() {
···
}
};
let genObj = obj.generatorMethod();
- 4、通过类定义中的生成器方法定义(可以是类声明或者类定义):
class MyClass {
* generatorMethod() {
···
}
}
let myInst = new MyClass();
let genObj = myInst.generatorMethod();
21.2.2 生成器扮演的角色
生成器可扮演三种角色:
1、迭代器(数据生产者):每一个
yield
都可以通过next()
返回一个值,这意味着生成器可通过循环和递归生成一组值。由于生成器实现了Iterable
接口(该接口在关于迭代的那一章有讲解),因此这组值可以传给 ECMAScript 6 中任何支持迭代的结构。例如:for-of
循环和扩展操作符(...)。2、观察者(数据消费者):
yield
也可以从next()
中得到值(通过向next()
方法传入一个参数)。这意味着生成器变成了数据消费者:暂停执行直到一个新的值通过next()
传入。3、协同程序(数据生产者和消费者):生成器有了暂停执行的能力,和既能成为数据生产者又能成为数据消费者的能力之后,实现协同程序(同时执行多个任务)就不需要做太多的工作了。
下一节更深入地讲解了这三种角色。