【JSAPI】Generator函数

2021/09/08 18:06:16

Generator 函数的特性

Generator 函数的返回值为一个 Iterator 对象,需要手动调用 next 方法让函数执行

Generatoryield 表达式等于为 js 提供了手动的惰性求值功能

Generator 函数是分段执行的,yield 表示暂停,next 表示恢复执行

调用 Generator 函数,返回一个遍历器对象

Generator 函数从暂停状态到恢复运行,它的上下文状态(context)是不变的。通过 next 方法的参数,就有办法在 Generator 函数开始运行之后,继续向函数体内部注入值

Generator 是实现状态机的最佳结构。

function* dataConsumer() {
  console.log("Started");
  console.log(`1. ${yield 111}`);
  console.log(`2. ${yield 222}`);
  return 333;
}

let g = dataConsumer();

g.next(1); // 参数无效,打印Started,返回值为{value: 111, done: false}
g.next(2); // 参数传递给上一个yield表达式,打印1.2,返回值为{value: 222, done: false}
g.next(3); // 参数传递给上一个yield表达式,打印1.3,返回值为{value: 333, done: false}

第一次执行 next 返回对象的 value 为第一个 yield 右侧的表达式运行结果,这时的参数是无效的

第二次执行 next 返回对象到 value 为第二个 yield 右侧的表达式运行结果,这时 next 的参数会作为第一个 yield 表达式的返回值

第三次执行 next ,此时已经没有 yield 关键字了,返回对象的 value"result",这时 next 的参数会作为第二个 yield 表达式的返回值

此后执行 next 的返回值皆与第三次相同

yield 表达式

  • 右侧表达式 右侧表达式的值会作为每次调用 next 方法后返回对象的 value 值
  • 返回值 yield 表达式的返回值为下一次调用 next 方法时的参数

next 方法

  • 参数 next 的参数会被当做上一个 yield 表达式的返回值,所以在第一次使用 next 方法时,传递参数是无效的,从语义上讲,第一个 next 方法用来启动遍历器对象,所以不用带有参数

return 表达式

运行到最后一个 yield 之后再调用 next 方法,返回对象的 value 就是 return 的值,同时返回对象的 done 变为 true

yield* 表达式

用于在一个 Generator 函数里面执行另一个 Generator 函数

从语法角度看,如果 yield 表达式后面跟的是一个遍历器对象,需要在 yield 表达式后面加上星号,表明它返回的是一个遍历器对象

function* g1() {
  yield "g1_1";
  yield "g1_2";
  yield "g1_3";
}

function* g2() {
  yield "g2_1";
  yield* g1();
  yield "g2_2";
}

let a = g2();
a.next(); // g2_1
a.next(); // g1_1
a.next(); // g1_2
a.next(); // g1_3
a.next(); // g2_2

[...g2()]; // ["g2_1", "g1_1", "g1_2", "g1_3", "g2_2"]

Generator 函数与 for...of 循环

for...of 循环可以自动遍历 Generator 函数运行时生成的 Iterator 对象,且此时不再需要调用 next 方法

for...of 循环时直接遍历 yield 返回的值,一旦 next 方法返回对象的 donefalse 就立即停止且不包含该返回对象,所以 return 的值不会被遍历出来

function* foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}

for (let v of foo()) {
  console.log(v);
}
// 1 2 3 4 5