调用生成器函数会返回一个生成器对象,每次调用生成器对象的 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 ()); console .log (g.next ()); console .log (g.next ()); console .log (g.next ()); console .log (g.next ());
首先调用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 ()); console .log (g.next ('param-a' )); console .log (g.next ('param-b' )); console .log (g.next ('param-c' )); console .log (g.next ());
定义一个生成器函数,并创建一个生成器对象。
第一次调用 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 }
。
从上述的分析中可以得出两个结论:
当我们为 next 传递值进行调用时,传入的值会被当作上一次生成器函数暂停时 yield 关键字的返回值处理。
当生成器函数执行完毕时,原本本次调用 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 , sent : "" , done : false , stop ( ) { this .done = true ; }, }; return { next (param ) { ctx.sent = param; const value = fn (ctx); 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' ));