基本概念

Generator 函数可以理解为是一个包含了多个内部状态的状态机;此外,执行 Generator 函数会返回一个遍历器对象,是一个遍历器对象生成器,返回的遍历器可以依次遍历 Generator 函数内部的每一个状态

Generator 函数与普通函数有几点不同:

  • function 命令与函数名之间有个 * 号,函数内部使用 yield 语句定义不同的内部状态

    1
    2
    3
    4
    5
    function* exampleGenerator() {
    yield 'hello'
    yield 'world'
    return 'lalala'
    }
  • 调用后函数并不执行,返回的也不是运行结果,而是一个指向内部状态的遍历器对象,通过调用 next 方法可以移向下一个 yield;每次调用 next 方法,就会返回一个有着 value 和 done 两个属性的对象

    1
    2
    3
    4
    5
    6
    const example = exampleGenerator()

    example.next() // => { value: 'hello', done: false }
    example.next() // => { value: 'world', done: false }
    example.next() // => { value: 'lalala', done: true }
    example.next() // => { value: undefined, done: true }

yield 语句实际上是暂停遍历的“标志”,next 方法遇到 yield 语句就暂停执行后面的操作,并将紧跟在 yield 后面的表达式的值作为返回对象的 value 属性,这个求值是惰性求值

需要注意,如果 yield 语句用在另一个表达式中,必须放在圆括号里,如果用于函数参数或者位于赋值表达式右边则可以不加

1
2
3
4
5
6
7
8
function* example() {
console.log('hello' + (yield 'world'))
}
------------------------------------------------------------
function* example() {
func(yield 'a')
let b = yield c
}

next 方法可以带一个参数,该参数会被当做上一条 yield 语句的返回值,通过这种方法可以在 Generator 函数运行的不同阶段从外部向内部注入不同的值从而调整函数行为

1
2
3
4
5
6
7
8
9
10
11
function* example(x) {
let a = 2 * (yield (x + 1))
let b = yield (a / 3)
return (x + a + b)
}

const exam = example(5)

exam.next() // => { value: 6, done: false }
exam.next(12) // => { value: 8, done: false }
exam.next(20) // => { value: 49, done: true }

在上面的代码中,第一次调用 next 返回 x + 1 的值,第二次调用传入参数 12,将上一步 yield 的值设置为 12,因此 a = 24;第三次传入 20,同样的,b = 20,因此最终返回 49

需要注意的是,next 方法的参数表示上一条 yield 语句的返回值,因此第一次使用 next 方法时传递参数是无效的,如果想在第一次调用 next 方法时就能输入值,可以在 Generator 函数外面在包一层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function wrapper(generatorFn) {
return (...args) => {
let generatorObj = generatorFn(...args)
generatorObj.next()
return generatorObj
}
}

const example = wrapper(function* () {
console.log(`${ yield }`)
return 'done'
})

example().next('hello world')
// hello world
// { value: 'done', done: true }

使用 for…of 循环可以自动遍历 Generator 函数生成的 Iterator 对象,不需调用 next 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
function* fn() {
yield 1
yield 2
yield 3
yield 4
yield 5
return 6
}

for (let i of fn()) {
console.log(i)
}
// 1 2 3 4 5

方法

Generator.prototype.throw()

Generator.prototype.return()

yield* 表达式