有网友碰到这样的问题“async-await内部原理-promise中自动执行next()-cojs实现”。小编为您整理了以下解决方案,希望对您有帮助:
解决方案1:
async-await:generator+Promise中自动执行next()-实现的异步化同步的语法糖
源码/***slice()reference.*/varslice=Array.prototype.slice;//返回一个新的数组对象,这一对象由begin和end索引组成(不含end)。原数组不含被改变/***Expose`co`.*导出co函数*/module.exports=co['default']=co.co=co;/***Wrapthegivengenerator`fn`intoafunctionthatreturnsapromise.*把给定的生成器函数fn包裹在promise函数里面并返回*Thisisaseparatefunctionsothatevery`co()`calldoesn'tcreateanew,unnecessaryclosure.*这是一个单独的函数,所以每个'co()'调用不会被新建,也不必要闭包**@param{GeneratorFunction}fn*@return{Function}*@apipublic*/co.wrap=function(fn){//包裹一个generatorFunction,保存,后面执行createPromise.__generatorFunction__=fn;returncreatePromise;functioncreatePromise(){returnco.call(this,fn.apply(this,arguments));}};/***Executethegeneratorfunctionorageneratorandreturnapromise.*执行这个生成器或者生成器函数,并返回一个promise**@param{Function}fn*@return{Promise}*@apipublic*/functionco(gen){varctx=this;//保存上下文varargs=slice.call(arguments,1);//常见的收集剩余参数//wewrapeverythinginapromisetoavoidpromisechaining,//我们包裹所有东西在一个promise之中去避免promise变化//whichleadstomemoryleakerrors.//这个导致内存泄漏的错误//seehttps://github.com/tj/co/issues/180returnnewPromise(function(resolve,reject){/*gen是函数说明是一个生成器函数,此时,执行生成器函数返回一个迭代器如果gen不是一个函数,则一般情况下是一个迭代器,否则直接resovle该值返回*/if(typeofgen==='function')gen=gen.apply(ctx,args);//把gen(...args)的方式转换为简写,方便下面的gen.next()if(!gen||typeofgen.next!=='function')returnresolve(gen);//迭代器开始自动执行onFulfilled();/***@param{Mixed}res*@return{Promise}*@apiprivate*/functiononFulfilled(res){varret;try{//执行到yield语句,返回yield语句右边的值,传入的res是上一个yield右边对应的Promise决议的值ret=gen.next(res);}catch(e){//出错则rejectreturnreject(e);}//已yield返回值为参数,执行next,进行下一次的迭代next(ret);returnnull;}/***@param{Error}err*@return{Promise}*@apiprivate*/functiononRejected(err){varret;try{//抛出异常,如果当前的yield在try中,则该异常由生成器内部捕获处理,否则由onRejected函数处理ret=gen.throw(err);}catch(e){returnreject(e);}next(ret);}/***Getthenextvalueinthegenerator,returnapromise.*获得next方法返回的value,并返回一个promise**@param{Object}ret*@return{Promise}*@apiprivate*/functionnext(ret){//迭代器执行完毕则返回,把valueresolve出去if(ret.done)returnresolve(ret.value);//把yield返回的值,也就是迭代器返回的值转成Promisevarvalue=toPromise.call(ctx,ret.value);if(value&&isPromise(value))returnvalue.then(onFulfilled,onRejected);returnonRejected(newTypeError('Youmayonlyyieldafunction,promise,generator,array,orobject,'+'butthefollowingobjectwaspassed:"'+String(ret.value)+'"'));}});}/***Converta`yield`edvalueintoapromise.*把yield返回的值,也就是迭代器返回的值转成Promise*@param{Mixed}obj*@return{Promise}*@apiprivate*///把obj转成PromisefunctiontoPromise(obj){if(!obj)returnobj;if(isPromise(obj))returnobj;//如果是一个生成器或者生成器函数,则进行coif(isGeneratorFunction(obj)||isGenerator(obj))returnco.call(this,obj);if('function'==typeofobj)returnthunkToPromise.call(this,obj);if(Array.isArray(obj))returnarrayToPromise.call(this,obj);if(isObject(obj))returnobjectToPromise.call(this,obj);returnobj;}/***Convertathunktoapromise.**@param{Function}*@return{Promise}*@apiprivate*//*把thunk函数转成Promise,thunk函数具体可参考thunkify库,实际是一个偏函数,最后一个参数需要一个回调,如下代码所示。*/functionthunkToPromise(fn){varctx=this;returnnewPromise(function(resolve,reject){/*fn是一个偏函数,里面包裹这一个异步函数,此时他还需要最后一个参数,也就是回调函数。如果不调用回调则该Promise无法决议。执行fn的时候,一般是一个异步的操作,比如readFile读取文件,然后读取完毕后会执行回调,在回调了执行该Promise的resolve或者reject*/fn.call(ctx,function(err,res){if(err)returnreject(err);if(arguments.length>2)res=slice.call(arguments,1);resolve(res);});});}/***Convertanarrayof"yieldables"toapromise.*Uses`Promise.all()`internally.**@param{Array}obj*@return{Promise}*@apiprivate*/functionarrayToPromise(obj){//把数组里的每个元素都转成Promise,Promise.all等待该数组中的所有的Promise决议returnPromise.all(obj.map(toPromise,this));}/***Convertanobjectof"yieldables"toapromise.*Uses`Promise.all()`internally.**@param{Object}obj*@return{Promise}*@apiprivate*/functionobjectToPromise(obj){varresults=newobj.constructor();varkeys=Object.keys(obj);varpromises=[];for(vari=0;i<keys.length;i++){varkey=keys[i];varpromise=toPromise.call(this,obj[key]);//把对象的值转成Promise,如果不能转,则直接记录该值if(promise&&isPromise(promise))defer(promise,key);elseresults[key]=obj[key];}returnPromise.all(promises).then(function(){//等待所有Promise的决议,然后返回resultsreturnresults;});functiondefer(promise,key){//predefinethekeyintheresultresults[key]=undefined;promises.push(promise.then(function(res){results[key]=res;}));}}/***Checkif`obj`isapromise.**@param{Object}obj*@return{Boolean}*@apiprivate*/functionisPromise(obj){return'function'==typeofobj.then;}/***Checkif`obj`isagenerator.**@param{Mixed}obj*@return{Boolean}*@apiprivate*/functionisGenerator(obj){return'function'==typeofobj.next&&'function'==typeofobj.throw;}/***Checkif`obj`isageneratorfunction.**@param{Mixed}obj*@return{Boolean}*@apiprivate*/functionisGeneratorFunction(obj){varconstructor=obj.constructor;if(!constructor)returnfalse;if('GeneratorFunction'===constructor.name||'GeneratorFunction'===constructor.displayName)returntrue;returnisGenerator(constructor.prototype);}/***Checkforplainobject.**@param{Mixed}val*@return{Boolean}*@apiprivate*/functionisObject(val){returnObject==val.constructor;}逻辑图原理Generator函数的暂停执行的效果,意味着可以把异步操作写在yield表达式里面,等到调用next方法时再往后执行。这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在yield表达式下面,反正要等到调用next方法时再执行。所以,Generator函数的一个重要实际意义就是用来处理异步操作,改写回调函数。
前面说过,Generator就是一个异步操作的容器。它的自动执行需要一种机制,当异步操作有了结果,能够自动交回执行权。
function*main(){varresult=yieldrequest("http://some.url");varresp=JSON.parse(result);console.log(resp.value);}functionrequest(url){makeAjaxCall(url,function(response){it.next(response);//此调用会在genarator函数的对应的yield,将其变成response这个值});}varit=main();it.next();//这里调用会执行到暂停右上部分,就是执行到第一个yield的右上部,//下一次执行next加上值时,会把值带入这个yield正常异步会写成下面模样。co模块,是将我们手动next的方法放在了自己封装的函数返回的promise之中,这样子变相变成了自调用
varfs=require('fs');varreadFile=function(fileName){returnnewPromise(function(resolve,reject){fs.readFile(fileName,function(error,data){if(error)returnreject(error);resolve(data);});});};vargen=function*(){varf1=yieldreadFile('/etc/fstab');varf2=yieldreadFile('/etc/shells');console.log(f1.toString());console.log(f2.toString());};原文:https://juejin.cn/post/7099803635780960270