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、协同程序(数据生产者和消费者):生成器有了暂停执行的能力,和既能成为数据生产者又能成为数据消费者的能力之后,实现协同程序(同时执行多个任务)就不需要做太多的工作了。

下一节更深入地讲解了这三种角色。

results matching ""

    No results matching ""