JavaScript-生成器函数

生成器函数

JavaScript 依赖于单纯程执行模型,导致的问题是服务请求未完成前,UI 页面都在等待渲染状态,用户看到是空白页面。
生成器函数能生成一组值的序列,每个值的生成都是基于每次请求,并不同于标准函数那样立即生成。

yield 用于生成独立的值

1
2
3
4
5
6
7
8
9
10
11
// 创建一个生成器函数
function* SnacksGenerator() {
yield 'cookie';
yield 'dried fruit';
yield 'chips'
}

// for-of 循环取出生成的值序列
for (let snack of SnacksGenerator()) {
console.log(snack)
}

yield* 把执行权交给下一个生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 定义生成器一
function* Ingredient() {
yield 'chilies';
yield* SnacksGenerator(); // 执行权交给 SanacksGenerator
yield 'mushroom';
yield 'lamb';
yield 'sesame sauce';
yield 'peppercorns';
}

// 定义生成器二
function* SnacksGenerator() {
yield 'cookie';
yield 'dried fruit';
yield 'chips'
}

for(var item of Ingredient()) {
assets('assets', item !== null, item)
}

for-of 循环取出生成器中生成的值序列,迭代器进行迭代的语法糖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 定义一个生成器,可生成 ingredient 序列
function* Ingredient() {
yield 'chilies';
yield 'mushroom';
yield 'lamb';
yield 'sesame sauce';
yield 'peppercorns';
}

/**
* 调用生成器 ingredient 创建一个迭代器 _ 以下是 for-of 的实现
let IngredientIterator = Ingredient()
let item; // 保存生成器产生的值

// 迭代器进行迭代的语法糖
while(!(item = IngredientIterator.next()).done) {
assets('assets', item !== null, item.value)
}
*/

// 这里 for-of 注意是遍历生成器的值的序列,并不是迭代器哦
for(item of Ingredient()) {
assets('assets', item !== null, item.value)
}

调用生成器,不会直接执行生成器,而是产生一个迭代器对象与生成器通信。迭代器对象暴露的最基本的接口是 nextnext 用来向生成器求一个值,从而控制生成器,遇到 yield 返回中间对象。多次循环,直至 {value: undefined, done: true}

生成器与迭代器工作流

    flowchart TD
    invokeGenerator[调用生成器] -->|"Generator()"| iterator("迭代器 Iterator")
    iterator -->|"next()"| yield{生成器中是否还有yield?}
    yield -->|yes| Yes("
    {
        value: 'cookie', // 结果值
        done: false // 指示器
    }")
    yield -->|no| No("
    {
        value: undefined, // 结果值
        done: true // 指示器
    }")
    No -->|"done: ture"| End

生成器与迭代器通信

yield / next

1
2
3
4
5
6
7
8
9
10
11
// 生成器传参
function* Generator(param){
yield param
}

// 为生成器提供一个初始值 initialValue
let Iterator = Generator('initialValue')
// 用 next 方法向生成器请求一个值
Iterator.next()
// 可以用 next 方法为等待中的 yield 表达式提供值
Iterator.next('is wait yield?')

next 方法可以为等待中的 yield 提供值,所以第一个 next 方法传参无效,因为第一次执行生成器代码,没有等待中的 yield.

try-catch

迭代器通过 throw 方法向生成器抛出异常

1
2
3
4
5
6
7
8
9
10
11
12
// 创建生成器
function* Generator() {
try {
yield 'try-catch'
}catch(e) {
console.log(e)
}
}

let Iterator = Generator()
Iterator.next()
Iterator.throw('throw an exception')

生成器的执行状态

1
2
3
4
5
6
7
function* GeneratorFn(name) {
yield name + ' Suspended'
yield name + ' Executing'
}
let iterator = GeneratorFn()
iterator.next()
iterator.next()

执行上下文跟踪生成器函数

1
2
3
4
5
// 创建一个生成器函数
function* generator(snack) {
yield snack + 'Production'
}
var iterator = generator('chips')

调用生成器函数之前的应用状态


调用生成器函数之后的应用状态


生成函数执行之后

生成函数执行之后,generator 函数执行上下文出栈,但不被销毁,因为 iterator 保持着对 generator 函数执行上下文的引用


调用跌代器的 next 方法

1
2
3
4
5
6
// 创建一个生成器函数
function* generator(snack) {
yield snack + 'Production'
}
var iterator = generator('chips')
var res = iterator.next()

再次激活 Generator 执行上下文,并推入栈。从上一次执行位置之后继续执行


iterator.next() 执行之后

生成器挂起,返回生成器表达式的值与生成器状态值对象


最后再执行 iteractor.next() 没有 yield 表达式,return {value: undefined, done: true} 生成器进入结束状态。