问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

async-await内部原理-promise中自动执行next()-cojs实现

发布网友 发布时间:2024-10-05 03:24

我来回答

1个回答

热心网友 时间:2024-12-01 00:03

async-await:generator+Promise中自动执行next()-实现的异步化同步的语法糖

源码/***slice()reference.*/varslice=Array.prototype.slice;//返回一个新的数组对象,这一对象由begin和end索引组成(不含end)。原数组不含被改变/***Expose`co`.*导出co函数*/mole.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
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
使命召唤9打不开,每次都出现这个情况,怎么弄啊 使命召唤9无法进入关卡 百度知道百度知道的作用 梦见在水里走什么预兆 梦见光脚走路是什么兆头 西装如何穿搭才有高级感? 西装怎样搭配才能穿出又飒又优雅的气势? 西装如何搭配才能穿出优雅成熟的气质? 我考了两次科目三路考,很不辛没能通过,我想放弃了,纠结?学车很辛苦... 我考过不了科目三 ...岁的时候去逝了,在他的教育问题上我不知道该怎么办,能帮帮我吗... 宝俊710车什么样 ...与小天狼星遇到摄魂怪,快死的时候,到底是谁救了他们。 中怎样培养学生语言实践能力 啊pp是什么意思? 核酸报告PP是什么意思? 求大佬估计逆战下一轮武器换购大概时间 男人在外喝酒我从不微信发表达关心的话,我认为没用,我就不喜欢发虚假... 埋堆堆自动续费会员在哪关 自动续费会员关闭方法 花甲要泡多久才会开口 和老总晚上一起喝酒了!我给他发微信了没回什么意思! 月季花如何种植方法 保养车基本都保养什么项目 保养车需要换什么东西 坐飞机可以带水杯吗 坐飞机是否可以带水杯 甲状腺癌晚期症状有哪些 最严重的甲状腺癌症状是什么 “当台湾茶遇上古琴”小雪雅集举办圆满成功 为什么不要独生女做老婆? 再保险分出人再保险分出人 Hypocrite [Bonus Track]的歌词是什么? 人中穴穴位简介 提莫大魔王卷土重来怎么获得魔王之心 谦字是什么意思? 环境保护小报资料 我的奇瑞瑞麟M1自动波挂档挂不上档,求解 请问麦博M200十周年纪念版音量问题 比学赶帮超对物业的影响 请教 麦博M-200(10周年纪念版)线控上的麦克风插孔怎么没反映_百度知 ... 麦博M200十周年纪念版和增强版有何区别? 辉县冯庄十字有没有办摩托车驾驶证的? 亚洲象的特点是什么 五笔输入法怎么用?快速掌握的输入方法,纯纯干货,值得收藏 亚洲象怎麽生活 郑州北环审验驾驶证去哪里审?都需要带什么,需要多少费用? 亚洲象的生活环境是什么 李亮亮陕西省延安市宝塔区冯庄乡新庄科村83号证号610602197803214418查查... 亚洲象生活习性 柯南中赤井秀一在491前做什么 西安十八度灰艺术文化传播有限公司怎么样?