ES6系列之Generator
学习 Generator 语法,你需要了解function* 、yield、next三个基本概念。
function* 用来声明一个函数是生成器函数,它比普通的函数声明多了一个*,*的位置比较随意可以挨着 function 关键字,也可以挨着函数名
yield 产出的意思,这个关键字只能出现在生成器函数体内,但是生成器中也可以没有yield 关键字,函数遇到 yield 的时候会暂停,并把 yield 后面的表达式结果抛出去
next作用是将代码的控制权交还给生成器函数
//声明生成器函数function*generator(){//Ayield'foo'//B}//获取生成器对象letg=generator();//第一个next(),首次启动生成器g.next();//{value:"foo",done:false}//唤醒被yield暂停的状态g.next();//{value:undefined,done:true}1.2 过程分析
//分析一个简单例子function*helloGenerator(){yield"hello";yield"generator";return;}varh=helloGenerator();console.log(h.next());//{value:'hello',done:false}console.log(h.next());//{value:'generator',done:false}console.log(h.next());//{value:'undefined',done:true}
创建了h对象,指向helloGenerator的句柄
第一次调用next(),执行到"yield hello",暂缓执行,并返回了"hello"
第二次调用next(),继续上一次的执行,执行到"yield generator",暂缓执行,并返回了"generator"。
第三次调用next(),直接执行return,并返回done:true,表明结束
经过上面的分析,yield实际就是暂缓执行的标示,每执行一次next(),相当于指针移动到下一个yield位置
总结一下,Generator函数是ES6提供的一种异步编程解决方案。通过yield标识位和next()方法调用,实现函数的分段执行
1.3 yield 表达式yield是Generator函数的暂缓执行的标识,对于yield只能配合Generator函数使用,在普通的函数中使用会报错
Generator函数中还有一种yield*这个表达方式
function*foo(){yield"a";yield"b";}function*gen(x,y){yield1;yield2;yield*foo();yield3;}varg=gen();console.log(g.next());//{value:1,done:false}console.log(g.next());//{value:2,done:false}console.log(g.next());//{value:"a",done:true}console.log(g.next());//{value:"b",done:true}console.log(g.next());//{value:"3",done:true}
当执行yield*时,实际是遍历后面的Generator函数,等价于下面的写法:
function*foo(){yield"a";yield"b";}function*gen(x,y){yield1;yield2;for(varvalueoffoo()){yieldvalue;}yield3;}
注意:yield 后面只能适配Generator函数
二、Generator应用场景2.1 异步操作的同步化表达Generator函数的暂停执行的效果,意味着可以把异步操作写在yield表达式里面,等到调用next方法时再往后执行。这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在yield表达式下面,反正要等到调用next方法时再执行。所以,Generator函数的一个重要实际意义就是用来处理异步操作,改写回调函数
function*loadUI(){showLoadingScreen();yieldloadUIDataAsynchronously();hideLoadingScreen();}varloader=loadUI();//加载UIloader.next()//卸载UIloader.next()
上面代码中,第一次调用loadUI函数时,该函数不会执行,仅返回一个遍历器。下一次对该遍历器调用next方法,则会显示Loading界面,并且异步加载数据。等到数据加载完成,再一次使用next方法,则会隐藏Loading界面。可以看到,这种写法的好处是所有Loading界面的逻辑,都被封装在一个函数,按部就班非常清晰
通过Generator函数部署Ajax操作,可以用同步的方式表达。
function*main(){varresult=yieldrequest("http://some.url");varresp=JSON.parse(result);console.log(resp.value);}functionrequest(url){makeAjaxCall(url,function(response){it.next(response);});}varit=main();it.next();2.2 控制流管理
//异步函数functiongetDataAsync(url){returnnewPromise((resolve,reject)=>{setTimeout(()=>{varres={url:url,data:Math.random()}resolve(res)},1000)})}
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。