当前位置: 首页 > 后端技术 > Node.js

从0开始一个Promise(1)

时间:2023-04-03 14:03:58 Node.js

背景Promise是一个非常实用的异步编程解决方案。本文对Promise源码的分析,可以加深读者对Promise原理的理解。以后再也不用怕异步编程,不用担心回调地狱了,Promise的使用会更舒服;另外,阅读源码也是提升自身编码能力的有效手段。本文将从实践到原理对Promise进行一次彻底的剖析,希望对读者有所帮助。文章组织本文将分为四个部分介绍第一部分:Promise链触发原理,以及resolve()、reject()、then()的实现第二部分:promise在实践中的几个特点及实现第三部分:Promise的几个主要类方法和对象方法的实现第四部分:Promise错误处理机制(一)Promise链触发原理,以及resolve()、reject()、then()的实现varpromiseObj=newPromise(function(resolve,reject){setTimeout(function(){resolve('tothen1');},1000);});promiseObj.then(function(val){console.log(val);return'tothen2';}).then(function(val){console.log(val);});以上是处理异步操作的简单承诺。在初始化中有一个函数包含setTimeout异步操作。一个then中定义了一个异步操作后的两个执行函数;promise的作用是在异步操作完成后触发then中声明的函数,替代之前不断定义回调的方式,通过同步写入实现异步操作。使程序编写符合开放封闭原则;整个实现过程分为几个步骤:Promise实例化Promise.prototype.then()创建promise链resolve递归触发promise链1.Promise实例化和一些实用函数下面是下面几个用到的实用函数,它们的功能有已在评论中解释过//声明一个空函数来初始化没有执行逻辑的承诺varnoop=function(){};varIS_ERROR={};varLAST_ERROR=null;//将a作为fn的参数执行functiontryCallOne(fn,a){try{returnfn(a);}catch(e){LAST_E错误率=e;返回IS_ERROR;}}//以a和b作为fn的参数执行函数tryCallTwo(fn,a,b){try{returnfn(a,b);}catch(e){LAST_ERROR=e;返回IS_ERROR;}}下面是promise的构造函数Promise(fn){/**_deferredState:用来表示promise是否添加了deferred*_deferred:deferred对象的原型是Handler*_state:promise执行状态pengind:0,fulfilled:1*_value:延迟函数执行参数*/this._deferredState=0;this._deferred=null;这个._state=0;这个._value=null;doResolved(this,fn);}其中_deferred对象包含一个promise对象then中定义的执行成功(resolved)和执行失败(rejected)时的延迟执行函数相当于一个promise链,其构造函数为followingHandler//我是延迟对象函数的构造函数Handler(onFulfilled,onRejected,promise){this.onFulfilled=typeofonFulfilled==='function'?onFulfilled:空;this.onRejected=typeofonRejected==='函数'?onRejected:空;this.promise=promise;}第一个属性已经在注释中解释过了。实例化的第一步是执行doResolved函数。在实例化promise的时候,会传入一个包含异步操作的函数,暂时叫作fn。该函数包含两个参数,这两个参数在执行成功或失败时使用调用then中函数的函数(有点绕哈哈);而doResolved的作用就是为这个fn传入这两个参数functiondoResolved(self,fn){tryCallTwo(fn,function(val){resolve(self,val);},function(val){reject(self,val);});}以上就是promise实例化的全过程2.promise.prototype.then的原理then的作用其实就是创建一个promise链,先把then中声明的函数包装在promise1中,然后return一个没有执行逻辑的promise2,然后将后者then中声明的函数包裹在promise2中,从而构造一个promise链Promise.prototype.then=function(onFulfilled,onRejected){varres=newPromise(noop);//anemptypromisehandle(this,newHandler(onFulfilled,onRejected,res));//在当前promise中包含一个promisereturnres;};Handler前面已经讲过了,我们来看看handle函数做了什么==0){self._deferredState=1;self._deferred=延迟;返回;}}handle函数传入两个参数,一个是promise本身,一个是deferred对象,包含一个没有处理逻辑的空promise,整个处理逻辑就是将deferred对象赋值给promise的deferred属性,然后将_deferredState切换为1,表示promise已经添加了deferred对象。handle说白了就是在声明的promise中添加一个包含空promise的deferred对象,然后通过then函数返回这个空promise。这样就形成了一个层层包裹的promise链。3.resolve&rejectfunctionreject(self,newValue){self._state=2;self._value=newValue;if(self._deferredState===1){handleResolved(self,self._deferred);}}函数解析(self,newValue){self._state=1;self._value=newValue;if(self._deferredState===1){handleResolved(self,self._deferred);}}resolve和reject函数记录了从外部传递过来的参数,并在_state属性中记录promise的状态,供下一个handleResolved函数触发promise链。4.handleResolvedfunctionhandleResolved(self,deferred){asap(function(){varcb=self.state===1?deferred.onFulfilled:deferred.onRejected;varret=tryCallOne(cb,self._value);resolve(deferred.promise,ret);return;})}首先,handleResolve本质上是一个递归函数,结束条件为self._deferredState!==1,即直到deferred对象的sub-promise为未安装。此外,执行延迟对象内部函数的代码被包装在asap模块中。asap模块相当于一个插队功能。其中包含的任务会在当前线程所有排队的任务队列执行完后执行,但会在新线程之前执行。即在setTimeout中的任务之前。之前在知乎上看到过关于这个问题的讨论,真相是这么快。可以看出,这个handleResolved函数的作用是根据_state属性,一一触发promise链中的onFulFilled或者onRejected函数。5.总结以上是阅读promise源码,对promise整体功能进行精简后的代码和总结。之后,我们会根据promise的几个特点完善各个功能,还给您一个完整的promise。请点击此处获取源代码。6.来一个promise测试functiontimeout(){varpromiseObj=newPromise(function(resolve,reject){console.log('1');resolve();});returnpromiseObj;}timeout().then(function(){setTimeout(function(){console.log('2')},500);}).then(function(){console.log('3')});console.log('4');