详解 ECMAScript 6 中的生成器(1)生成器的基本概念
- UID
- 1066743
|
详解 ECMAScript 6 中的生成器(1)生成器的基本概念
生成器(Generator)是 ECMAScript 6 中引入的新概念。生成器本身是一个很强大的工具,应该出现在每个 JavaScript 开发人员的工具箱之中。不过,生成器目前主要被使用在 JavaScript 框架和第三方库中,而在日常开发中的使用较少。这主要是因为生成器的用法并不容易掌握。本文的目标是让更多的开发人员了解生成器,并使用这一工具在日常开发中简洁高效地完成一些任务。本文中的示例代码在 NodeJS 8.10 上测试运行。生成器在浏览器的支持请见参考资源。
生成器的基本概念在讨论生成器的具体用法之前,先从生成器的基本概念开始进行介绍。与生成器相关的概念有两个:
生成器对象的最大特点在于它们的执行可以被暂停和继续。在 JavaScript 中,我们并不能控制普通函数的执行。对于一个函数,当它开始执行之后,就会一直执行到结束,并把返回值传递给调用者。考虑到 JavaScript 引擎执行时的单线程特性(不考虑到使用 WebWorker 的情况),在一个函数的执行过程中,是不能终止该函数的。如果你的函数中不小心引入了无限循环,那么整个应用的执行都会被阻塞。
我们先从最简单的生成器函数开始,如代码清单 1 所示。生成器函数与普通函数的差别在于 function 和函数名称之间的星号(*)。生成器函数中使用 yield 表达式来产生值。
清单 1. 基本的生成器函数1
2
3
4
5
| function *sample() {
yield 1;
yield 2;
yield 3;
}
|
在调用代码清单 1 中的生成器函数之后,得到的是一个生成器对象。生成器对象在其 next 方法被调用时,可以依次返回多个不同的值。这些返回值通过 yield 来声明。代码清单 1 中使用 yield 来产生了 1,2 和 3 共 3 个值。当 next 方法调用时,这些值会被依次返回。在代码清单 2 中,首先从生成器函数 sample 中创建了一个新的生成器对象 func。该对象 func 的执行一开始是被暂停的。当 next 方法调用时,func 对象开始执行,并执行到第一个 yield 表达式,并把结果 1 返回给调用者。返回值是一个包含了两个属性 value 和 done 的对象。属性 value 包含的是 yield 表达式所产生的值,而 done 用来表示是否还有更多值可以被获取。再次调用 next,可以继续 func 的执行,并执行到第二个 yield 表达式。如此循环,直到第四个 next 方法调用,done 的值才变为 true,表明已经没有更多值可以被获取了。
清单 2. 使用生成器对象1
2
3
4
5
6
7
8
9
10
11
| let func = sample();
func.next();
// -> {value: 1, done: false}
func.next();
// -> {value: 2, done: false}
func.next();
// -> {value: 3, done: false}
func.next();
// -> {value: undefined, done: true}
func.next();
// -> {value: undefined, done: true}
|
生成器的强大之处正是来源于可以暂停和继续生成器对象执行的能力。每个生成器对象都可以被看成是一个状态机。同一个生成器函数所创建的每个对象都在内部维护自己的状态,彼此并不会互相影响。调用 next 方法会继续生成器的执行,触发内部的状态转换,运行到下一个 yield 表达式所在的位置。接着执行会被暂停,等待下一次 next 方法的调用。代码清单 3 中创建了生成器函数 sample 的 2 个不同的实例。调用其中一个对象的 next 方法并不会影响到另一个对象的内部状态。
清单 3. 同一个生成器函数创建的不同生成器对象1
2
3
4
5
6
7
8
| let func1 = sample();
let func2 = sample();
func1.next();
// -> {value: 1, done: false}
func2.next();
// -> {value: 1, done: false}
func1.next();
// -> {value: 2, done: false}
|
|
|
|
|
|
|