ES6学习笔记之Generator函数

Learning Card

  • Generator函数是什么
    • 状态机+遍历器
    • 可以暂停的函数
    • 解决异步
  • Generator函数的特征
    • function与函数名直接有星号
    • 函数内部有yield表达式
    • 函数用next()语句调用
  • yield返回值
    • next()调用返回的对象的value值是yield后面的值
    • 如果没有,就返回undefined
  • next()传参
    • next()中的参数作为上一个yield语句的返回值
    • 第一个next()传的参数总是无效

Generator函数是什么

Generator函数也是解决异步操作的一种方案。阮大大在《ES6入门》里给Generator下了两个定义,一个是它是“一个状态机,封装了多个内部状态”,第二个是它还是一个“遍历器对象生成器”,可以遍历Generator函数内部的每一个状态。

这些术语听起来有点复杂,个人觉得Blue在ES6的课程里说的解释比较容易记住,Generator函数是一个可以中途停下来的函数,你push它一下,它就往前进一步。

Generator函数的特征

Generator函数的写法和普通函数有三个不同:

  1. function关键字和函数名之间有一个星号(星号的位置可以挨着function,也可以挨着函数名,也可以在function和函数名直接,但是不能function*函数名三个连起来
  2. 函数内部有yield表达式,让函数执行过程中可以停下来
  3. 函数的调用必须使用next()
1
2
3
4
5
6
7
8
function show() {
console.log("a");
yield;
console.log("b");
}
let s = show();
show.next(); // "a"
show.next(); // "b"

yield返回值

yield既可以传参,又可以返回值,我们先来看看yield如何返回值。

从上面那段代码中可以看到,Generator是一个遍历器对象,调用Generator函数并不会立即执行,而是返回一个指向内部状态的指针对象,如果我们将show.next()打印出来,可以看到实时上,它是一个包含了两个属性的对象,一个value属性,它的值是当前yield表达式的值,另一个是done属性,它的值是一个布尔值,表示遍历是否结束。如果当前yield没有值,那么value属性的值就是undefined

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function * helloWorldGenerator() {
console.log('a');
yield 'hello';
console.log('b');
yield 'world';
console.log('c');
return 'ending';
}

let hw = helloWorldGenerator();
hw.next() //{value: 'hello', done: false}
hw.next() //{value: 'world', done: false}
hw.next() //{value: 'ending', done: true}
hw.next() //{value: undefined, done: true}

如果我们画一个图来看每一次指针指向的对象的值(即遇到的需要暂停的yield后面的值)会更加清晰

yield划分

三种颜色表示了三次调用Generator函数的内容。

next传参

前面我们提到了,yield后面的值会作为yield之前的对象的value值返回出去,如果yield后面没有值,那么返回的value就是undefined。但是Generator函数可以通过next()传递参数到函数内部,作为上一个yield表达式的返回值。还是用helloWorldGenerator这个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function * helloWorldGenerator() {
let a = yield 'hello';
console.log(a);
let b = yield a;
console.log(b);
return b;
}

let hw = helloWorldGenerator();
hw.next(1)
console.log(hw.next(1)) //{value: 'hello', done: false}
hw.next(2) //2
console.log(hw.next(2)) //{value: 2, done: false}
hw.next(3) //3
console.log(hw.next(3)) //{value: 3, done: true}

我们还是画图来看这个流程

next()传递参数

可以看出,第一个next()语句因为之前没有出现yield语句,所以第一个next()传的参数总是无效。


ES6学习笔记的内容主要基于阮一峰老师的《ECMAScript 6入门》,原文请见链接