前言Promise是js中非常重要的一部分。只有理解了promise,才能更好的理解async、await和generator。但是很多时候,我就是不了解promise的机制,所以这次我一步步实现一个promise,加深印象,提升思路。大概架子通过我们经常写的promise语法,我们可以先写一个大概的架子。Promise接受回调并调用它们。它有pending、onFulfilled、onRejected三种状态,resolve函数可以使pending状态变为OnFulfilled状态,同样,reject函数可以将pending状态变为onRejected状态。我们首先实现了上面的描述部分。constPromiseCopy=function(fn){this.info={status:"pending",value:"",};constself=this;self.onFulfilledArr=[];//then函数self中第一个回调函数的集合.onRejectedArr=[];//then函数中第二个回调函数的集合constresolve=function(value){//加上这个判断是为了表示只有在pending状态才会执行//状态变成了onFulfilled后不能再改//符合2.1.2.1if(self.info.status==="pending"){self.info.status="onFulfilled";self.info.value=value;inPromiseA+self.onFulfilledArr.forEach((fn)=>fn(value));}};//同上,符合PromiseA+,2.1.3.1constreject=function(value){if(self.info.status==="pending"){self.info.status="onRejected";self.info.value=value;self.onRejectedArr.forEach((fn)=>fn(value));}};fn(解决,reject);};resolve的额外实现其实我们的resolve函数还有一些功能没有在这里实现。我们知道在调用resolve(x)时,x的值有几种情况。如下,如果x是Promise实例本身,会抛出错误如果x是一个Promise对象,那么then函数的执行取决于这个x的状态,如果x也调用了resolve(y),其中y是也是一个promise对象,那么then函数的执行依赖于promise对象,依此类推,直到结束如果x是一个thena,promise状态就会改变ble对象是包含then属性的对象,或者是包含then静态方法的函数,then直接执行then函数。如果x是普通值,直接变成onFulfilled状态,执行后面的then函数想想网上的大实现。部分resolve是上面的代码,但是按照规范,上面的几点应该在resolve函数中进行判断,所以我们上面写的代码是错误的。还有一个问题就是我们需要判断x是否在resolve函数中。不是实例本身,而是我们经常给普通的resolve函数传入一个参数,所以中间肯定有一个中间函数,看下面代码constPromiseCopy=function(fn){this.info={status:"pending",value:"",};constself=this;self.onFulfilledArr=[];//then函数中第一个回调函数的集合self.onRejectedArr=[];//then函数中第二个回调函数的集合//_resolve是我们经常调用的resolve//但是真正的实现应该是里面的resolveconst_resolve=function(value){//这个函数需要改一下//PromiseCopy一旦被实例化,那么self就是实例本身resolve(self,value);};//此时我们可以在resolve中判断constresolve=function(promise,value){letifexec=false;//先判断value是否为promise本身if(value===promise){//一定要用TypeError来写否则promises-aplus-tests是不行的//记住这是第一个陷阱,promises-aplus-tests只识别izesTypeErrorreject(newTypeError("ApromisecannotbeonFulfilledwithitself."));}//valueisathenableobject//这个需要Object.prototype.toString.call(value)的错误形式===[objectObject]”判断//否则resolve([])有问题,不知道是不是我实现的问题if(value&&(Object.prototype.toString.call(value)==="[objectObject]"||typeofvalue==="function")){//varpromise1=Promise.resolve(dump).then(function(){//return{//then:(resolve,reject)=>{//setTimeout(()=>{//resolve({//then:(resolve,reject)=>{//resolve("aa111a");//throw"other";//},//});//});//},//};//});//promise1.then(//(res)=>{//console.log(res==="aa111a");//console.log("aaa");//},//(res)=>{//console.log(res);//console.log("error");////);//try--catch这里一定要加上,不然promises-aplus-tests会一直报错,这是第三个大坑//因为promises-aplus-test测试里面有这个//看上面的注释例子try{//获取then函数constthen=value.then;//如果then是一个函数,就执行这个函数if(typeofthen==="function"){//为什么要.call(value,x,y)可以自己试试原生的Promise这种情况下,this指向的是value,所以需要绑定//因为then我们已经取出了then=value.then,直接调用then(),this指向的是window//为什么还要绑定后面的两个函数//根据原生的Promise,thenable中的then函数可以是accept两个函数resolve,reject//下面的.then操作只有在手动调用resolve和reject后才会执行。具体可以自己操作。then.call(value,function(value){if(ifexec){return;}//ifexec一定要加上,否则会报200ms错误。第四大坑//目的是为了防止多次执行,而语言无法表达它。请参阅以下示例//varpromise1=Promise.resolve(dump).then(function(){//return{//then:(resolve,reject)=>{//resolve("aa111a");//resolve("aa111a");//},//};//});ifexec=true;resolve(promise,value);},function(value){if(ifexec){return;}ifexec=true;reject(value);});return;}}catch(e){if(ifexec){return;}ifexec=true;reject(e);}}//下面一点很重要,就是async,await和saga等一些插件的核心//如果x是一个promise对象,那么thenExecution取决于x的状态//这个判断一定要放在这里,不要跟上面的改,否则promises-aplus-tests超过200ms会报错,记住这是第二个坑ue//但是没那么简单,我们需要明白两点//如果价值的promise不再是pending,我们再给他也没用,所以需要直接调用if(value.info.status==="pending"){value.onFulfilledArr=self.onFulfilledArr;value.onRejectedArr=self.onRejectedArr;}//如果值状态为onFulfilledif(value.info.status==="onRejected"){self.info.value=value.info.value;self.onRejectedArr.forEach((fn)=>fn(value.info.value));}//如果值状态为rejectif(value.info.status==="onFulfilled"){self.info.value=value.info.value;self.onFulfilledArr.forEach((fn)=>fn(value.info.value));}return;}//如果是普通值//加上这个判断,表示只会在pending状态下执行//状态变成onFulfilled后,就不能再改变了//Conformto2.1.2.1inPromiseA+if(self.info.status==="pending"){self.info.status="onFulfilled";self.info.value=value;self.onFulfilledArr.forEach((fn)=>fn(value));}};//同上原因符合PromiseA+,2.1.3.1//reject没有resolve那么多规则,比较简单constreject=function(value){if(self.info.status==="pending"){self.info.status="onRejected";self.info.value=value;self.onRejectedArr.forEach((fn)=>fn(value));}};//这个时候fn调用了什么是_reoslve//这个trycatch主要是为了实现promiseCopy.prototype.catchtry{fn(_resolve,reject);}catch(e){setTimeout(()=>{self.onRejectedArr.forEach((fn)=>fn(e));});}};上面我们介绍的then的实现是promise的resolve用法,promise的另一个基本用法是接在then后面,因为是.then,所以我们想到的就是thisthen方法挂在Prototype中,然后你可以在新的PromiseCopy时得到这个。里面有两个函数,一个是onFulfilled之后执行的回调,一个是onRejected之后执行的回调。现在的问题是他怎么让then中的函数在resolve和reject之后执行呢?这种延迟执行或者在特定情况下执行,就是我们所说的观察者模式。下面我们用代码来实现上面的话,在代码中写上详细的注释。PromiseCopy.prototype.then=function(onFulfilled,onRejected){constself=this;//这里要判断,如果PromiseCopyresolve了,那么直接执行onFulfilledif(self.info.status==="onFulfilled"){setTimeout(()=>{onFulfilled(self.info.value);});}if(self.info.status==="onRejected"){setTimeout(()=>{onRejected(self.info.value);});}//根据PromiseA+中的2.2.1.1和2.2.1.2,onFulfilled和onRejected必须是函数,否则会被忽略(()=>{onFulfilled(self.info.value);});});}if(typeofonRejected==="function"){self.onRejectedArr.push(()=>{setTimeout(()=>{onRejected(self.info.value);});});}//根据PromiseA+2.2.7规范then函数必须返回一个promise对象returnnewPromiseCopy((resolve,reject)=>{});};then的附加实现上面then的实现也是一个简单的用法,但是根据PromiseA+的规范,then函数中还有几点没有实现。看代码解释promise2=promise1.then(onFulfilled,onRejected);promise2.then(onFulfilled,onRejected);onFulfilledinpromise1.then,如果onRejected函数返回一个x,会被当作[[Resolve]](promise2,x),和上面的resolve一样,注意如果函数什么都不返回,就是返回的undefinedpromise1.then函数只要两个回调函数之一报错,然后直接调用promise2的错误回调in.thenfunction如果promise1.then的第一个回调不是函数,promise1调用resolve,那么promise2.then的第一个回调参数就是promise1中resolve函数的throw值。同理,如果promise1.then的第二个回调不是函数,promise1调用了reject,那么promise2.then中的error回调就会被执行想如果像上面这样,当新抛出的promise调用这个resolve或者reject是一个关键,抛出的promise的执行取决于onFulfilled和onRejected的返回值。写诺言的时候,我想了很久。我不知道如何组织它。后来想不通了。看了网上很多文章,发现这些逻辑是在PromiseCopy的body中实现的returnnewPromiseCopy((resolve,reject)=>{});然后实现增强版的PromiseCopy.prototype.then=function(onFulfilled,onRejected){constself=this;//必须这样写才能让值passonFulfilled=typeofonFulfilled==="function"?onFulfilled:(val)=>val;//这个一定要这么写,一定会抛错ronRejected=typeofonRejected==="function"?onRejected:(err)=>{throwerr;};constnewnewPromise=newPromiseCopy((resolve,reject)=>{if(self.info.status==="onFulfilled"){setTimeout(()=>{try{//如果onFulfilled不是一个functionresolve--self.info.valueletvalue=self.info.value;//这个注释不需要,只是记录一下当时的思路//这个添加是为了防止then函数的回调不被调用一个函数,但是一个字符串//if(typeofonFulfilled==="function"){//value=onFulfilled(value);//}value=onFulfilled(value);//这里做一个[[Resolve]](promise2,x)processing//因为resolve是直接在里面做的,所以直接调用,和网上有些实现有点不一样//他们提取了一个resolvePromise函数调用,我直接调用resolresolve(value);}catch(e){reject(e);}});}//注意根据上面,onFulfilled和onRejected抛出的值必须经过[[Resolve]](promise2,x)//这个不同于resolve和reject,promise中的resolve会走[[Resolve]](promise2,x),reject不走if(self.info.status==="onRejected"){setTimeout(()=>{try{let{value}=self.info;value=onRejected(self.info.value);resolve(value);}catch(e){reject(e);}});}//状态为pending时也需要Pushif(self.info.status==="pending"){self.onFulfilledArr.push((data)=>{setTimeout(()=>{try{letvalue=data;value=onFulfilled(value);resolve(value);}catch(e){reject(e);}});});self.onRejectedArr.push((data)=>{setTimeout(()=>{try{letvalue=data;value=onRejected(data);resolve(value);}catch(e){reject(e);}});});}});returnnewPromise;};综上,到这里已经完成了promise的主要实现,下面是Promise其他静态方法的测试结果Promise.resolvePromiseCopy.resolve=function(data){returnnewPromiseCopy((resolve,reject)=>{resolve(data);});};rejectPromise.reject=function(reason){returnnewPromise((resolve,reject)=>{reject(reason));});};Promise.all这个方法有几个特点如下:这个方法接受一个rray,数组的每个元素都是一个promise对象。只有当所有的promise都onFulfilled时,then回调才会被执行,结果的顺序与数组的顺序一致。如果其中一个承诺被拒绝ct然后会返回这个值PromiseCopy.all=function(data){letcount=0;//记录调用次数lettotal=data.length;letresult=[];returnnewPromiseCopy((resolve,reject)=>{for(leti=0;i
