generator函数生成器及其实现
Published in:2023-04-24 | category: 前端 面试

调用生成器函数会返回一个生成器对象,每次调用生成器对象的 next 方法会执行函数到下一次 yield 关键字停止执行,并且返回一个 { value: Value, done: boolean }的对象。本质是协程在 ES6 的实现。

基础语法

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

let g = gen();

console.log(g.next()); // { value: 1, done: false }
console.log(g.next()); // { value: 2, done: false }
console.log(g.next()); // { value: 3, done: false }
console.log(g.next()); // { value: undefined, done: true }
console.log(g.next()); // { value: undefined, done: true }
  • 首先调用gen() 生成器函数返回 g 生成器对象。
  • 其次返回的 g 生成器对象中拥有一个 next 的方法。
  • 每当我们调用**g.next()**方法时,生成器函数紧跟着上一次进行执行,直到函数碰到 yield 关键值。
  • yield 关键字会停止函数执行并将 yield 后的值返回作为本次调用 next 函数的 value 进行返回。
  • 同时,如果本次调用g.next()导致生成器函数执行完毕,那么此时 done 会变成 true 表示该函数执行完毕,反之则为 false 。

进阶语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function* gen() {
const a = yield 1;
console.log(a, 'this is a');
const b = yield 2;
console.log(b, 'this is b');
const c = yield 3;
console.log(c, 'this is c');
return 'res';
}

let g = gen();

console.log(g.next()); // 打印 { value: 1, done: false }
console.log(g.next('param-a')); // 打印 param-a this is a { value: 2, done: false }
console.log(g.next('param-b')); // 打印 param-b this is b { value: 3, done: false }
console.log(g.next('param-c')); // 打印 param-c this is c { value: 'res', done: true }
console.log(g.next()); // 打印 { value: undefined, done: true }
  • 定义一个生成器函数,并创建一个生成器对象。
  • 第一次调用 g.next()方法时,函数执行到 yield 1并中断函数执行,由于代码中断,a的赋值语句并没有被执行。
  • 第二次调用g.next()方法时,next 接受传参传入一个字符串param-a,函数执行时从上一次中断的地方开始继续执行,也就是执行 a的赋值语句,从打印结果来看,a被赋值为字符param-a
  • 第三次调用g.next()方法并传入字符串param-b时,结果同样是变量b被赋值为param-b
  • 第四次调用g.next()方法与第二第三次的区别是,return语句作为返回值语句取代了yield语句,所以执行结果是{ value: 'res', done: true }
  • 第五次调用g.next(),由于生成器函数已经执行完毕,所以打印{ value: undefined, done: true }

从上述的分析中可以得出两个结论:

  1. 当我们为 next 传递值进行调用时,传入的值会被当作上一次生成器函数暂停时 yield 关键字的返回值处理。
  2. 当生成器函数执行完毕时,原本本次调用 next 方法返回的{done:true,value:undefined}由于return语句变为了{ done:true,value:'res'}

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
const generatorWrap = (fn) => {
const ctx = {
next: 0, // 表示下一次执行生成器函数状态机switch中的下标
sent: "", // 表示next调用时候传入的值 作为上一次yield返回值
done: false, // 是否完成
// 完成函数
stop() {
this.done = true;
},
};
return {
next(param) {
// 1. 修改上一次yield返回值为context.sent
ctx.sent = param;
// 2.执行函数 获得本次返回值
const value = fn(ctx);
// 3. 返回
return {
done: ctx.done,
value,
};
},
};
};

function gen() {
let a, b, c;
return generatorWrap(function (ctx) {
switch ((ctx.prev = ctx.next)) {
case 0:
ctx.next = 1;
return 1;

case 1:
a = ctx.sent;
console.log(a, 'this is a');
ctx.next = 2;
return 2;

case 2:
b = ctx.sent;
console.log(b, 'this is b');
ctx.next = 3;
return 3;

case 3:
c = ctx.sent;
console.log(c, 'this is c');

case 'end':
return ctx.stop();
}
})
}

const g = gen();

console.log(g.next());
console.log(g.next('param-a'));
console.log(g.next('param-b'));
console.log(g.next('param-c'));
Prev:
性能监控、异常监控
Next:
CDN