深入浅出 Generator

概念基本

形式上,Generator 是一个函数,但具有两个区别于普通函数的特征:

  • function 关键字和函数名之间有一个星号(*)

  • 函数体内部使用 yield 表达式,定义不同的内部状态(yield 代表产出)

内涵上可以将其理解为一个状态机 + 遍历器对象

 function* helloWorldGenerator() {
    yield 'hello'
    yield 'world'
    return 'ending'
}

var 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 }

调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着valuedone两个属性的对象。value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束

yield 表达式

由于 Generate 函数返回的遍历器对象,只有调用 next 方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield 表达式就是暂停标志。

遍历器对象的 next 方法的运行逻辑如下:

(1)遇到 yield 表达式,就暂停执行后面的操作,并紧跟在 yield 后面的那个表达式的值,作为 Generate 返回的对象的 value 属性值

(2)下一次调用 next 方法时,再继续往下执行,直到遇到下一个 yield 表达式

(3)如果没有再遇到新的 yield 表达式,就一直运行到函数结束,直到 return 语句为止,并将 return 语句后面的表达式的值,作为返回的对象的 value 属性值

(4)如果该函数没有 return 语句,则返回的对象的 value 属性值为 undefined

需要注意的是,yield 表达式后面的表达式,只有当调用 next 方法、内部指针指向该语句时才会执行,因此等于 js 提供了手动的“惰性求值“的语法功能。

例如:

function* gen() {
    yield 123 + 456
}

yield 后面的表达式 123 + 456,不会立即求值,只会在 next 方法将指针移到这一句时,才会求值。

tips:yield 表达式只能用在 Generator 函数中

next 方法的参数

yield 表达式本身没有返回值,或者说总是返回 undefinednext 方法可以带一个参数,该参数就会被当作一个 yield 表达式的返回值

function*  f() {
    for(var i = 0; true; i++) {
        var reset = yield i
        if (reset) i = -1
    }
}

var g = f()

g.next() // { value: 0, done: false }
g.next() // { value: 1, done: }
g.next(true) // { value: 0, done: false } 

上面代码先定义了一个可以无限运行的 Generate 函数 f如果 next 方法没有参数,每次运行到 yield 表达式,变量 reset 的值总是 undefined。当 next 方法带一个参数 true 时,变量 reset 就被重置为这个参数(即 true),因此 i 会等于 -1,下一轮循环就会从 -1 开始递增。

简单使用

Generator 函数中的 yield 语句执行可以通过 next 顺序执行,yield 本身没有返回值,当 yield 语句用 next 执行时会返回 yield 语句返回的值。可以通过 next 传入值给 yield,从而使它具有返回值

Last updated