参考:
1、https://www.runoob.com/w3cnote/es6-generator.html
2、https://blog.csdn.net/ganyingxie123456/article/details/78152770
generator---生成器
yield--放弃
next---下一步
- 可以通过yield关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供了解决方案。
- 可以运用yield作为中断执行函数的方法,运用next执行yield之下的程序。
- 可以理解为Generator是个状态机,封装了多个内部状态,需要暂停的地方都用
- 调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。
1、Generator函数组成
Generator 有两个区分于普通函数的部分:
- 在 function 后面,函数名之前有个 * ;
- 函数内部有 yield 表达式。
- 其中 * 用来表示函数为 Generator 函数,yield 用来定义函数内部的状态。
-yield
(1)yield语句只能用于function* 的作用域,如果function* 的内部还定义了其他的普通函数,则函数内部不允许使用yield语句。
(2)yield语句如果参与运算,必须用括号括起来。
-用途:
yield后面的程序是在操作数据,而我们要先请求数据之后才能操作,因为请求数据,有的快有的慢。那为了保证一定能够获取到数据之后再执行,就可以使用我们这个generator函数了。
- yield就是中断它之下的程序
- generatorObj.next() 执行yield后面的程序
function* func(){ console.log("one"); yield '1'; console.log("two"); yield '2'; console.log("three"); return '3'; }
2、执行机制
调用 Generator 函数和调用普通函数一样,在函数名后面加上()即可,但是 Generator 函数不会像普通函数一样立即执行,而是返回一个指向内部状态对象的指针,所以要调用遍历器对象Iterator 的 next 方法,指针就会从函数头部或者上一次停下来的地方开始执行。
f.next(); // one // {value: "1", done: false} //第一次调用next函数,从Generator函数的头部开始执行,先是打印了one, //执行到yield就停下来,并将1作为返回对象的value属性值, //此时函数还没有执行完,返回对象的done属性值是false f.next(); // two // {value: "2", done: false} f.next(); // three // {value: "3", done: true} //第三次调用 next 方法时,先是打印了 three ,然后执行了函数的返回操作,并将 return 后面的表达式的值,作为返回对象的 value 属性值,此时函数已经结束,多以 done 属性值为true 。 f.next(); // {value: undefined, done: true} //第四次调用 next 方法时, 此时函数已经执行完了,所以返回 value 属性值是 undefined ,done 属性值是 true 。如果执行第三步时,没有 return 语句的话,就直接返回 {value: undefined, done: true}。
3、函数返回的遍历器对象的方法
1、 next方法
一般情况下,next 方法不传入参数的时候,yield 表达式的返回值是 undefined 。当 next 传入参数的时候,该参数会作为上一步yield的返回值。
function* sendParameter(){ console.log("strat"); var x = yield '2'; console.log("one:" + x); var y = yield '3'; console.log("two:" + y); console.log("total:" + (x + y)); }
next不传参
var sendp1 = sendParameter(); sendp1.next(); // strat // {value: "2", done: false} sendp1.next(); // one:undefined // {value: "3", done: false} sendp1.next(); // two:undefined // total:NaN // {value: undefined, done: true} //next传参 var sendp2 = sendParameter(); sendp2.next(10); // strat // {value: "2", done: false} //此处输入的20会作为上一步的yield的返回值 sendp2.next(20); // one:20 // {value: "3", done: false} sendp2.next(30); // two:30 // total:50 // {value: undefined, done: true}
除了使用 next ,还可以使用 for... of 循环遍历 Generator 函数生产的 Iterator 对象。
2、return方法
- return 方法返回给定值,并结束遍历 Generator 函数。
- return 方法提供参数时,返回该参数;不提供参数时,返回 undefined 。
//generator函数 function* foo(){ yield 1; yield 2; yield 3; } //执行 var f = foo(); //第一次执行 f.next(); // {value: 1, done: false} // //return中有参数则返回此参数 f.return("foo"); // {value: "foo", done: true} // //此时return已经把generator函数停止了 f.next(); // {value: undefined, done: true}
3、throw方法
//throw 方法 throw 方法可以在 Generator 函数体外面抛出异常,在函数体内部捕获。 var g = function* () { try { yield; } catch (e) { console.log('catch inner', e); } }; // var i = g(); i.next(); // try { i.throw('a'); i.throw('b'); } catch (e) { console.log('catch outside', e); } // catch inner a // catch outside b
遍历器对象抛出了两个错误,第一个被 Generator 函数内部捕获,第二个因为函数体内部的catch 函数已经执行过了,不会再捕获这个错误,所以这个错误就抛出 Generator 函数体,被函数体外的 catch 捕获。(太神奇了吧。。勉强理解)
4、yield* 表达式
yield* 表达式表示 yield 返回一个遍历器对象,用于在 Generator 函数内部,调用另一个 Generator 函数。
function* callee() { console.log('callee: ' + (yield)); } function* caller() { while (true) { //返回一个遍历器对象,用在generator函数内部,调用另一个generator函数 yield* callee(); } } //一个generator函数 const callerObj = caller(); // //调用了calle callerObj.next(); // {value: undefined, done: false} //传入参数,作为上一步yield的返回值 callerObj.next("a"); // callee: a // {value: undefined, done: false} callerObj.next("b"); // callee: b // {value: undefined, done: false} //但是这里为什么调用了三次却只输出了2次呢?不是很懂 // 等同于for...of循环 function* caller() { while (true) { for (var value of callee) { yield value; } } }
使用场景
实现 Iterator
为不具备 Iterator 接口的对象提供遍历方法。
function* objectEntries(obj) { const propKeys = Reflect.ownKeys(obj); for (const propKey of propKeys) { yield [propKey, obj[propKey]]; } } const jane = { first: 'Jane', last: 'Doe' }; for (const [key,value] of objectEntries(jane)) { console.log(`${key}: ${value}`); } // first: Jane // last: Doe
Reflect.ownKeys() 返回对象所有的属性,不管属性是否可枚举,包括 Symbol。
jane 原生是不具备 Iterator 接口无法通过 for... of遍历。这边用了 Generator 函数加上了 Iterator 接口,所以就可以遍历 jane 对象了。