一、什么是generator函数
(1)generator本身是用来生成迭代器的。在async和await被官方正式引入标准前用来模拟async和await,作为一另种js异步编程解决方案。
generator函数执行过程中可以暂停(使用yield)再继续(调用遍历器对象next方法),暂停的时候将函数执行权交出给其他函数,比如异步操作,等其他函数执行完了以后,执行权会返回回来,此时可以拿到异步操作的结果,继续恢复执行。
//* 可以挨着function或者函数名,但是中间必须有空格function* myGenerator(){yield setTimeout(()=>{console.log('其他函数执行')},1000);yield new Promise((resolve)=>{setTimeout(()=>{resolve('resolved')},1000)})return 'value returned'}let myIterator = myGenerator()myIterator.next() //{value: 定时器id, done:false}myIterator.next() //{value: promise, done:false}myIterator.next() //{value: 'value returned', done:true}
(2)yield与return对比<br />yield可以暂停函数执行,并将后面表达式的值(**惰性求值**,遇到next方法后才会求值)作为返回对象的value值,可暂停多次<br />return可以返回值,直接终止函数执行<br />(3)next方法<br />next方法的作用是,**推动函数执行**,如果遇到yield,函数交出执行权,<br />next参数为**上一次yield表达式的值**。第一次调用next方法时传的参数是在generator函数中接受不了的,因为第一次调用next方法,遇到yield,函数就暂停运行,yield表达式并没有进行求值计算。
function *gen(){let a = yield 123 + 567;console.log(a, 'a')let b = yield 123 + 678;console.log(b,'b')}let iter = gen();iter.next() // 函数开始执行,遇到yield暂停iter.next(2) // =>2 'a' 上一次yield方法表达式为iter.next(3) // =>3 'b'// 如果没有传参,yield表达式的值为undefined
二、迭代器和遍历
如果用遍历来执行一些函数,遍历不会中断,只能在每次迭代中增加逻辑,迭代器可以精确控制每一次迭代,可以避免无意义的迭代。
使用场景:中间件的迭代调用,中间件一般会有next方法,只有调用next方法后,才会执行下一个中间件。
路由钩子router.beforeEach,axios拦截器中都有next方法,调用next方法才会进入下一步。
遍历和迭代好比上楼梯,遍历是走过每一层楼,整个过程是连续的,迭代是中间的一个动作,迭代一次,上一层楼。
三、手写generator函数
核心是函数返回一个对象,每次调用迭代器对象的next方法,迭代数组一次
function generator (arr){let arrLenght = arr.length,currentIndex = 0;return {next(){return {value: arr[currentIndex++], done: currentIndex >= arrLenght}}}}let iterator = generator([1,2,3,4,5]);console.log(iterator)
四、generator与iterator在一系列中间件执行中的引用。
核心思想就是:将函数数组传入生成器生成迭代器,然后先调用init函数,进行第一次迭代,给函数传入next方法(调用迭代器next方法,将迭代结果传入是否迭代判断函数进行递归操作),调用数组里的函数。
;(function(functions){function *functGenerator(){for(let item of functions){//之前把item执行了,导致后面报错yield item}}let iterator = functGenerator();//这个函数是用来接收每次迭代返回的对象,根据done来判断是否进行迭代,并提供下一次迭代功能function checkNextDo(iteratorResult){//数组迭代最后到最有一个元素时,done还是为false,之后迭代返回对象的done才为true,//所以这里先调用函数还是先判断是否迭代结束都不影响if(iteratorResult.done){return}else{//没有迭代完成,接续迭代,此时需要传一个参数函数,提供下一次迭代功能,然后再判断//调用当前迭代到的函数iteratorResult.value(function(){checkNextDo(iterator.next())})}}function init(){checkNextDo(iterator.next())}init()})([function test1(next){console.log('test1')next()},function test2(next){console.log('test3')next()},function test3(next){console.log('test3')next()},function test4(next){console.log('test4')},])// test1// test2// test3
