当前位置: 首页 > Web前端 > JavaScript

Promise源码渐进解读(一)

时间:2023-03-27 00:53:55 JavaScript

关注前端小鱼,阅读更多原创技术文章。市场上有很多Promise库。本文选择了一个轻量级的Promisepolyfill来逐步实现分析。如果对Promise不熟悉,请先移步完整代码+注释,阅读参考。Promiseconstructor-sourcecode/**Promiseconstructor*参数fn:executorfunction(resolve,reject)=>{resolve(),reject()}*executorfunction再接收2个参数:resolve和rejectcallbackfunction*/functionPromise(fn){if(!(thisinstanceofPromise))thrownewTypeError('Promisesmustbeconstructedvianew')//如果实例对象不是Promise的实例,则抛出错误if(typeoffn!=='function')thrownewTypeError('notafunction')//如果传入的“执行函数”不是函数类型,则抛出错误/**内部状态码*0:pending,当前Promise正在执行,默认值*1:fulfilled,执行resolve函数,参数_value不是合约,即_valueinstanceofPromise===false*2:rejected,执行reject函数*3:fulfilled,resolve函数被执行,参数_value是一个合约,即_valueinstanceofPromise===true*/this._state=0//默认值为0/*是否处理过*/this._handled=false//默认没有处理过/*解析值/拒绝原因(resolve或reject参数)*/this._value=undefined//默认为undefined/**存储Handle实例对象数组,缓存then方法传入的回调*包含3个参数:onFulfilled和当前promise的onRejected回调方法,当前promise的完成fulfillment后要执行的promise*当前Promise的resolve()或reject()触发调用后,循环遍历_deferreds数组中的每个Handle实例,处理对应的onFulfilled和onRejected方法*/this._deferreds=[]/**调用doResolve()方法*参数fn:执行函数*参数this:(合约)实例*/doResolve(fn,this)}调用newPromise生成一个Promise实例,参数(执行函数)必须是函数类型Code内部设置状态,是否处理,解析值/拒绝原因,缓存then()方法传入的回调立即调用doResolve()方法doResolve()-源码/**doResolve()方法*参数fn:执行函数(resolve,reject)=>{resolve(),reject()}*参数self:(contract)instance*/functiondoResolve(fn,self){//console.log(self)vardone=false//初始化done以确保resolveOrrejectonlyonce/*Immediatelyex执行(非异步)传入的执行函数fn*/try{fn(//fn的resolve回调函数(值){if(done)returndone=true/***resolve()方法*参数self:(contract)instance*参数值:解析值*/resolve(self,value)},//fn的reject回调函数(reason){if(done)returndone=true/***resolve()方法*参数self:(contract)instance*参数值:拒绝原因*/reject(self,reason)})}catch(err){if(done)returndone=truereject(self,err)}}初始化done为false,一旦执行resolve或reject回调,设置为true即可ensure回调只执行一次,立即执行executor函数,这就确认了执行newPromise时的代码是同步的。立即调用resolve()(解决回调)或reject()(解决回调或抛出错误)方法如果抛出错误,则调用reject()方法resolve()-源码/**resolve()方法*parameterself:(contract)instance*parameternewValue:resolutionvalue*/functionresolve(self,newValue){//console.log(self,newValue)try{if(newValue===self)thrownewTypeError('一个承诺cannotberesolvedwithitself.')//解析后的值不能自己解析(否则会死循环)if(newValue&&(typeofnewValue==='object'||typeofnewValue==='function')//如果解析值是对象或函数对象){varthen=newValue.thenif(newValueinstanceofPromise){//解析值是promise类型,_state=3self._state=3self._value=newValuefinale(self)/*此时selfPromise{_state:3,_handled:false,_value:Promise{...},_deferreds:[]}*/return}elseif(typeofthen==='function'){//解析值为thenable对象(有then方法的对象或函数),并且它的then方法继续执行doResolve//console.log(newValue)//console.log(newValue.then)//console.log(self)//doResolve(function(){////整个函数作为一个新的executor方法,传递给doResolve()并立即调用,then()就是当前的resolve(),执行这个resolve()来执行then()//returnthen.apply(newValue,arguments)//将在then方法体中解析值this指向解析值本身,并执行then()//},self)doResolve(bind(then,newValue),self)//使用apply重写上面源码中的bind方法//doResolve(then,self)//then方法体中不要指定this,调用后this指向全局对象windowreturn}}self._state=1//解决方法value为其他正常值,_state=1self._value=newValue//将解决方案值赋给合约实例_value属性//console.log(self)/***finale()方法*参数self:(contract)instance*/finale(self)}catch(e){reject(self,e)}}resolve回调的解析值不能是合约实例本身,否则会造成死循环如果是Promise对象,合约的_stat会是分别给e和_value赋值,然后执行finale()方法。如果是thenable对象(非Promise),则继续在其then方法上执行doResolve(过程用bind方法重写,请参考注释,之前从未重写过start)整个函数传递将doResolve()作为executor方法,立即调用executor方法返回then.apply(newValue,arguments)将solutionvalue的then方法体中的this指向solutionvalue本身,然后执行then()如果不是以上2种类型,也分别赋值合约的_state和_value,执行finale()方法。如果抛出错误,调用reject()方法bind()-sourcecode/**PolyfillforFunction.prototype.bind*useapplywritebind方法*/functionbind(fn,thisArg){returnfunction(){fn.apply(thisArg,arguments)}}reject()-源代码/**reject()方法*参数self:(contract)instance*ParameternewValue:reasonforreject*/functionreject(self,newValue){self._state=2//拒绝时,_state=2self._value=newValue//将拒绝原因赋值给合约实例的_value属性finale(self)}的过程和resolve()大致一样,赋值分别为合约的_state和_value,然后执行finale()方法finale()-测试代码手写一个测试finale()方法,方便阶段测试/**finale()方法用于测试*参数self:(contract)instance*/functionfinale(self){console.log(self)if(self._state===1){console.log('resolve:'+self._value)}elseif(self._state===2){console.log('reject:'+self._value)}elseif(self._state===3){console.log('resolvevalueisPromise')}}newPromise-phasetest创建一个Promise实例,根据executor函数的回调类型(resolve/reject)和解析值orreasonforrejectionType,printdifferentresults/**Test:newPromise(()=>{})*第一次resolve或reject真正执行后,后面的resolve或reject不会执行,只测试结果mergedhere*/newPromise((resolve,reject)=>{resolve(3)//'resolve:3',resolvevalue是一个基本类型,/*self是Promise{_state:1,_handled:false,_value:3,_deferreds:[]}*/reject(3)//'reject:3',拒绝值是基本类型/*self是Promise{_state:2,_handled:false,_value:3,_deferreds:[]}*/resolve({val:3})//'resolve:[objectObject]',解析后的值为普通对象/*self是一个Promise{_state:1,_handled:false,_value:{val:3},_deferreds:[]}*/resolve(newPromise(()=>{}))//'resolvevalueisPromise',resolutionvalueisPromiseinstance/*selfisPromise{_state:3,_handled:false,_value:Promise{_state:0,_handled:false,_value:undefined,_deferreds:[]},_deferreds:[]}*/resolve({//resolvevalue是thenable对象,self是{value:3,then:[Function:then]}value:3,then:function(){/*在resolve()方法中,在then方法的主体中指定this。如果不指定,调用后this指向全局对象window,this.value指向undefined*/console.log(this)//{value:3,then:[Function:then]},this指向分辨率value本身console.log(this.value)//3},})console.log('nextcoding...')//'nextcoding...',只要不抛出错误就不会影响后续代码执行throwError('error!')//抛出错误})执行结果总结新Promise代码内部,执行器函数同步执行(立即)不会被捕获),但不影响其他代码.如果该值是一个thenable对象,则在其then方法中将this绑定到该对象并执行其then方法。到本节为止的代码→