当前位置: 首页 > 科技观察

从头实现一个Promise

时间:2023-03-13 15:45:17 科技观察

1.Promise/A+规范①Promise是一个类或函数,有3种内部状态,即pending(等待)、fulfilled(执行、完成)、rejected(拒绝、未完成)。默认是pending状态,即Promise对象在第一次创建时的状态是pending,pending状态可以转换为fulfilled或者rejected。已完成和已拒绝是最终状态。一旦完成或被拒绝,将无法更改为其他状态。②Promise需要对外提供then方法。如果可选参数onFulfilled和onRejected不是函数,则应忽略promise.then(onFulfilled,onRejected);onFulfilled和onRejected函数都应该异步执行;onFulfilled函数调用时,会将当前Promise的值作为参数传入,并且只能调用一次;调用onRejected函数时,会将当前Promise的失败原因作为参数传入,并且只能调用一次;then函数的返回值对于链式调用仍然是Promise;③resolvePromisethen方法会创建并返回一个Promise对象,then中注册的回调函数会返回各种值,必须进行验证。then方法返回的promise不能等于then中回调函数的返回值x,否则需要抛出错误;如果then回调函数的返回值是非Promise对象,那么直接使用then返回的promise对象的resolve方法,resolve(x)即可。如果then回调函数的返回值x是一个Promise对象或者是一个带有then方法的对象或函数,那么就需要执行它的then方法来注册回调,获取Promise或者Promise-like对象的值作为然后返回promise的值,如果该值仍然是Promise对象则需要递归操作;2、实现Promise①根据第一个规范,Promise是一个类或者函数,所以我们先将Promise定义为一个类,内部状态有3种,我们定义为常量。varPENDING="pending";//等待状态varFULFILLED="fulfilled";//执行,完成状态varREJECTED="rejected";//拒绝,未完成状态classPromise{constructor(){this.state=PENDING;//PromiseAfter对象创建完成,默认处于等待状态}??}②我们在创建Promise的时候,会传入一个函数,这个函数会在Promise对象创建的时候立即执行,并且会接收两个参数,用于分别执行或拒绝当前Promise对象,即修改当前Promise对象的状态。Promise用于异步处理,所以当Promise状态变成完成时,可能会收到异步操作执行的结果,当Promise状态变成未完成时,可能会收到失败原因,所以Promise里面需要保存。异步操作的结果值和失败的原因。......classPromise{constructor(executor){//传入执行器函数......this.value=undefined;//保存异步操作的结果this.reason=undefined;//保存failedReasonconstresolve=(value)=>{this.value=value;this.state=FULFILLED;//改变Promise对象的状态为完成状态}constreject=(reason)=>{this.reason=reason;this.state=REJECTED;//将Promise对象的状态改为未完成状态}try{executor(resolve,reject);//executor是用户传入的,可能会出错,需要捕获}catch(e){reject(e);}}}③这里还有一个问题,就是Promise必须执行一次。Promise的状态一旦从pending变成fulfilled或者rejected,就不能再改变,也不允许从fulfilled变成fulfilled。也就是说resolve或者reject只能执行一次。所以我们需要判断resolve和reject的内部结构。如果状态发生了变化,则不再执行,如:......classPromise{constructor(executor){//执行器函数介绍......constresolve=(value)=>{if(this.state===PENDING){//防止用户多次解析,以第一个解析为准...}}constreject=(reason)=>{if(this.state===PENDING){//防止用户多次拒绝...}}...}}④在Promise中添加一个then函数,then函数接收两个onFulfilled和onRejected函数作为参数,用于处理when的回调函数Promise完成和未完成的时间。如果不是函数,则必须初始化为函数,如:classPromise{then(onFulfilled,onRejected){onFulfilled=typeofonFulfilled==="function"?onFulfilled:(value)=>{//如果onFulfilled是notafunction,initializeacompletionhandlerreturnvalue;};onRejected=typeofonRejected==="function"?onRejected:(reason)=>{//如果onRejected不是function,初始化一个unfulfilled处理函数throwreason;//Throwwhateverpassed}}}⑤Then方法其实就是一个注册回调的过程。当随后调用的Promise对象状态变为完成状态时,可以执行onFulfilled回调函数,当Promise对象状态变为rejected时,可以执行onRejected回调函数。所以回调函数的执行取决于当时调用的Promise的状态。同时为了支持链式调用,then方法也需要返回一个Promise对象。按照之前的Promise规范,传入的回调函数必须异步执行,这里用setTimeout来模拟。classPromise{then(onFulfilled,onRejected){......letpromise;switch(this.state){caseFULFILLED://当then方法被调用时,当前Promise状态已经变成完成状态,回调可以完成后立即执行Functionpromise=newPromise((resolve,reject)=>{setTimeout(()=>{try{letx=onFulfilled(this.value);}catch(e){console.log(e);//打印错误消息reject(e);}});});break;caseREJECTED:promise=newPromise((resolve,reject)=>{setTimeout(()=>{try{letx=onRejected(this.reason);}catch(e){reject(e);}});}break;casePENDING:promise=newPromise((resolve,reject)=>{//TODO});break;}returnpromise;}}⑥当Promise对象调用then处于pending状态,此时通过then注册的回调函数并不能立即执行,注册的回调函数必须等待Promise的状态变为final状态,这就涉及到一个发布订阅模型,我们可以先保存回调函数,然后Promise什么时候变成fina我状态?也就是调用resolve或者reject的时候,所以我们可以把注册的回调函数取出来,在调用resolve或者reject的时候执行。classPromise{constructor(executor){constresolve=(value)=>{if(this.state===PENDING){//防止用户多次解析,以第一次解析为准......this.onFulfilleds。forEach(fn=>fn());//取出then中注册的完成回调函数执行}};constreject=(reason)=>{if(this.state===PENDING){//阻止用户来自运行过多的reject......this.onRejecteds.forEach(fn=>fn());//取出then中注册的拒绝回调函数并执行}};}then(onFulfilled,onRejected){.....switch(this.state){casePENDING:promise=newPromise((resolve,reject)=>{this.onFulfilleds.push(()=>{try{letx=onFulfilled(this.value);}catch(e){console.log(e);//打印错误信息reject(e);}});this.onRejecteds.push(()=>{try{letx=onRejected(this.reason);}catch(e){reject(e);}});});break;}}}⑦接下来就是处理这时注册的回调函数的返回值,因为回调函数的返回值可能是在各种情况下,它m可能是普通的值,可能是Promise对象,也可能是带有then方法的对象,所以我们要一一处理。这里我们使用一个单独的方法resolvePromise()来处理各种情况,比如//传入then()方法中创建的Promise对象,回调函数的返回值x,以及then中创建的Promise的resolve()method、rejectconstresolvePromise=function(promise,x,resolve,reject){//TODO}classPromise{constructor(executor){//引入执行函数...}then(onFulfilled,onRejected){caseFULFILLED:promise=newPromise((resolve,reject)=>{......letx=onFulfilled(this.value);resolvePromise(promise,x,resolve,reject);//处理回调函数的返回值});caseREJECTED:promise=newPromise((resolve,reject)=>{......letx=onRejected(this.reason);resolvePromise(promise,x,resolve,reject);//处理回调函数的返回值});casePENDING:this.onFulfilleds.push(()=>{letx=onFulfilled(this.value);resolvePromise(promise,x,resolve,reject);//处理回调函数的返回值});this.onRejecteds。push(()=>{letx=onRejected(this.reason);resolvePromise(promise,x,resolve,reject);//处理回调函数的返回值});}}3.实现resolvePromise①如果返回值回调函数的和then()方法中创建的Promise对象相同,就会抛出错误,相当于等待自己进入死循环。letp1=newPromise((resolve,reject)=>{resolve(1);})letp2=p1.then((value)=>{//p2是then方法returnp2中创建的Promise对象;});//结果抛出错误,显示Chainingcycledetectedforpromise#constresolvePromise=function(promise,x,resolve,reject){if(promise===x){//禁止解析自身thrownewError("Chainingcycledetectedforpromise#");}}②如果回调函数返回一个Promise对象或者一个带有then方法的类Promise对象,或者函数,因为函数可能也有then方法,那么我们需要把then方法取出来执行.对于Promise对象,then方法的执行会注册相应的回调函数,当Promise状态变为final状态后会执行相应的回调函数。执行回调函数后,就可以获取Promise对象的value值,然后通过调用then方法创建value值,创建Promise对象的值。constresolvePromise=function(promise,x,resolve,reject){......if((x&&typeofx==="object")||typeofx==="function"){//如果是一个对象或者一个function,函数里面可能还有一个then方法letexecuted;try{letthen=x.then;//尝试取出then方法if(typeofthen==="function"){//如果对象上有then方法,那么就是一个Promise对象或者包含一个thenmethodthen.call(x,function(y){//执行then方法的对象。对于真正的Promise对象,回调会被注册。状态改变后,回调函数会被执行,并且值为回调中可以接收到Promiseif(executed)return;executed=true;//注册的回调函数只能执行一次resolvePromise(promise,y,resolve,reject);//返回值也可以是Promiseobject,所以需要递归直到变成正常值},function(e){if(executed)return;executed=true;reject(e);});}else{//不包含的普通对象then方法可以直接解析resolve(x);}}catch(e){if(executed)return;executed=true;reject(e);}}else{resolve(x);}}四、实现catchcatch可以被视为一个特殊的then方法,它将调用then()方法,但只注册被拒绝的回调函数,这是then(onFulfilled,onRejected)和then(onFulfilled).catch(onRejected)的区别。如果onRejected写在then里,那么当then的onFulfilled出错的时候,onRejected就抓不到错误,如果写在catch里,就相当于下一个then()方法,所以可以catch之前的then()方法中发生的错误。classPromise{catch(onRejected){returnthis.then(null,onRejected);//只注册rejection的回调函数}}5.总结Promise其实是一个类,里面有state,value,reason等属性,用于存储当前Promise的状态,执行成功后的返回值,执行失败的原因,内部提供了resolve和reject两个方法,会以参数的形式传递给executor,也就是传递给在外部,以修改Promise状态。Promise还提供了then方法来注册回调函数。注册回调时,它与Promise的当前状态有关。如果是最终状态,则立即执行。如果处于waiting状态,则先保存,等待resolve或reject方法被调用。是时候取出回调并执行它了。注册的回调函数可能返回多种值:如果返回正常值,则直接使用then返回的Promise的resolve方法进行解析;如果它返回一个Promise对象或者一个带有then方法的对象或函数,那么你需要调用它的then方法并注册一个自定义回调来接收当前Promise的值。Promise变为final后,会执行callback获取其值,最终作为届时返回的Promise的值。那就是解决(x)。完整源码如下:varPENDING="pending";//等待状态varFULFILLED="fulfilled";//执行,完成状态varREJECTED="rejected";//拒绝,未完成状态//传入then中创建()方法Promise对象,回调函数的返回值x,then()方法中创建的Promise的resolve,rejectconstresolvePromise=function(promise,x,resolve,reject){if(promise===x){//禁止自己解析thrownewError("Chainingcycledetectedforpromise#");}if((x&&typeofx==="object")||typeofx==="function"){//如果是对象或者函数,函数也可能有then方法letexecuted;try{letthen=x.then;//尝试取出then方法if(typeofthen==="function"){//如果对象上有then方法,那么就是一个Promise对象或者是一个包含then方法的对象then.call(x,function(y){//执行then方法。对于真正的Promise对象,会注册一个回调。状态改变后,回调函数将被执行,并且可以在回调中接收Promise的值if(executed)return;executed=true;//注册的回调函数只能执行一次resolvePromise(promise,y,resolve,reject);//返回值也可能是Promise对象,所以需要递归直到变成正常值},function(e){if(executed)return;executed=true;reject(e);});}else{//不包含then方法的普通对象,resolve(x);}}catch(e){if(executed)返回;执行=真;拒绝(e);}}else{resolve(x);}}classPromise{constructor(executor){//引入执行器函数this.state=PENDING;//Promise对象创建后,默认为等待状态this.value=undefined;//保存异步操作的结果this.reason=undefined;//保存失败原因this.onFulfilleds=[];//保存then中注册完成的回调函数this.onRejecteds=[];//在then中保存注册拒绝回调函数constresolve=(value)=>{if(this.state===PENDING){//防止用户多次解析,以第一次解析为准this.value=value;this.state=FULFILLED;//将Promise对象的状态变为完成状态this.onFulfilleds.forEach(fn=>fn());//取出then中注册的完成回调函数并执行}};constreject=(reason)=>{if(this.state===PENDING){//防止用户多次拒绝this.reason=reason;this.state=REJECTED;//将Promise对象的状态改为未完成状态());//取出then中注册的rejection回调函数执行}};try{executor(resolve,reject);//executor是用户传入的,可能会出错,所以需要被捕获}catch(e){reject(e);}}then(onFulfilled,onRejected){onFulfilled=typeofonFulfilled==="function"?onFulfilled:(value)=>{//如果onFulfilled不是一个函数,初始化一个完成处理函数returnvalue;};onRejected=typeofonRejected==="function"?onRejected:(reason)=>{//IfonRejected如果不是函数,则初始化一个未完成的处理函数throwreason;//传什么就扔}letpromise;switch(this.state){caseFULFILLED://当调用then方法时,当前Promise状态已经变成完成状态,then立即执行的回调函数promise=newPromise((resolve,reject)=>{setTimeout(()=>{try{letx=onFulfilled(this.value);resolvePromise(promise,x,resolve,reject);}catch(e){console.log(e);reject(e);}});});break;caseREJECTED:promise=newPromise((resolve,reject)=>{setTimeout(()=>{try{letx=onRejected(this.reason);resolvePromise(promise,x,resolve,reject);}catch(e){reject(e);}});});break;casePENDING:promise=newPromise((resolve,reject))=>{this.onFulfilleds.push(()=>{try{letx=onFulfilled(this.value);resolvePromise(promise,x,resolve,reject);}catch(e){reject(e);}});this.onRejecteds.push(()=>{try{letx=onRejected(this.reason);resolvePromise(promise,x,resolve,reject);}catch(e){reject(e);}});});break;}returnpromise;}catch(onRejected){returnthis.then(null,onRejected);//只注册被拒绝的回调函数}}