《纯手写Promise由浅入深.docx》由会员分享,可在线阅读,更多相关《纯手写Promise由浅入深.docx(18页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、在我的上一篇文章里着重介绍了 async的相关知识,对promise的提及甚少,现在 很多面试也都要求我们有手动造轮子的能力,所以本篇文章我会以手动实现一个 promise的方式来开掘一下Promise的特点.简单版Promise首先我们应该知道Promise是通过构造函数的方式来创立的(newPromise( executor),并且为 executor 函数传递参数:fb(八ctio八 Prokvi(execiAtor) execiAtoiresolve, reject);fcmcti。八 reso/vcO fiAictioi reject0 (1再来说一下Promise的三种状态:pen
2、ding-等待,resolve-成功,reject-失败,其中 最开始为pending状态,并且一旦成功或者失败,Promise的状态便不会再改变, 所以根据这点:fb(八cti。八 Proii(execo(tor) let _jthis = this;= pending,;exccbctoir(Yeso(ve.biAd(this)rejcct.bind(tkis);fb(八cti。八 reso/vcO return 八cw八ctio八(reso/ve) setTiieout(_ = reso/vcC第二个.then 成功)50。).协6八(如= coisole.log(res)return
3、new ProM(fa八ctio八(reso/vCj reject) sctTiMCObd:( = %/。比,第三个失败。j 1OOO).tke八(res = rescoole.log(res,)j rej = coiasole.log(rejy)Promise 完善其实做到这里我们还有好多好多没有完成,比方错误处理/eject处理,catch实 现.all实现,.race实现,其实原理也都差不多,(all和race以及resolve和reject 其实返回的都是一个新的Promise),错误的传递?还有很多细节我们都没有考虑 到,我这里写了一个还算是比拟完善的:fb(八cti。八 PKOKi
4、(execatoK)(let _jthis = this;二,pe八dMg,;jthis.failCallBack = 八 defined;jthis.siACcessCallback = 八 defined;error =八 de 行八 cd;setTiieoiAt(_ = try (cxecatorQtkk.。八Resoc.bind(_tkis)j _this.o八Reject.bind(_this) catckx (e) _th/s.error = e;if Qthis.caHBackDefer & _this,caHBackDcfcY.fai() 上his.caUBackDefecfa
5、i eke if (#is._catch) _this._catch(c) else tkrow 八ew ErrorC八 catch1)!)Pirogi.prototype = constructor. Proiij0八ResoA/e:八cti。八匕 ms) if (this.$status =,pc八dMg) 协 is 辑st 仅 s = access;this.iresolve(pairavs)Lresolve:缶八田。八(pqr4Ms) let fhis = this;let successCallback = fhis.succcssCaMback;if (succeCallhack)
6、 _this,defcKsb(ccessCaHback.bMH(J;hisparai);)Idefer: fuMtiocallack) let fhis = this;let result;let defer = J;his.cal(BackDefer.siACCCSS;if (_this.辑status = fail1 & Ltkis.catckErrorF八c) defer = 【;)try (result = callBackQ) catch (e) result = e;defer = _jthis.caUBackDefw.faU;)if (result & result 沁仅八ceo
7、f Pkom) resbdt.the八(_this.ca(BackDefecsbiccesS, _this.callBackDefer.fail); return)defer(res/t)IoiReject:八ctio八(cKror) if (this.$stat(AS =,pc八ding) this .抖status = YaiP;this.匕3 ect(crv*or)breject:缶八ctio八(ckkok) let _this = this;s.cor = error;let failCallBack = Ihis.failCaMBack;let _catcly = )his._cat
8、ch;if (failCallBack) _thisdefc(fai(Ca(lBack.bMd(_this)error); else if (_catcl ,tch(cwor) eke (sctTiiMeoatJ = throw new Errorfuia catch promise1) /O),tkc八:Ftmcf70Hsaccess = 0 二) fail) let _this = thislet reset Fai I =。=e;if (fail) reset Fai I = fail;,his.catchEFb(八 c = true;)let 八ewFVohise = new Prom
9、/(_ = );_this.caHBackDefcr = success:八cwFVomMc.o八Rcsoe.bi八daiewProMsc)fail:hewPKOMke.o八尺ect.bihW(八cwProxke);_tissucces$Callhack - success;_this.failCaHBack = reset Fai I;iretuiri 八cwPron/use)catck. fuictioi(catchCalll3ack - () = ) this._catch = catchC allBack/测试代码task。/八(心= co 八 so/eog(4: + res)retu
10、rn 第一个 /e八).the八(res 二 return new Proii(res = setTikeoatJ = 匕es(第二个 tkc), 3000!).tkeH%s = co h so/coges)!).协0八(心= return new ProM(sg fail) = seEkeoatJ = fail。then 失败,b 400)!).the八(res 二 co 八 so/eog(7ko),扬。八(_ = & 0 = return new ProK/u(f八ctio八(reSj rej) sctTiMcot(_ = KejCpgMise reject1)3000)1)八0.the
11、八 0,/。八(_ = )可= co 八 so/cog(句);return rej + 处理完成,!).协0八(心= cosoledog(res);/故意出错co八so(e.log(ppppppp)1)协力”s = rej = co 八 so/eog(句);/再次抛错co 八 so/eog(0ooo0o)catch(e = co 八 so/e/og(e)还有一段代码是我将所有的.then全部返回调用者来实现的,即全程都用一个promise来记录状态存储任务队列,这里就不发出来了,有兴趣可以一起探讨下.有时间会再完善一下all, race, resoh/e.不过到时候代码结构肯定会改变,实在没
12、啥时间,所以讲究看一下吧,欢迎交流if (_this.辑status = pending。 松 tas = full1)fiAACtion reject0 if (_this.辑status =,pc八ding) _this.$status =吓aiP)其中$status来记录Promise的状态,只有当promise的状态未pending时我们 才会改变它的状态为full或者fail:因为我们在两个status函数中使用了 this,显 然使用的是Promise的一些属性,所以我们要绑定rescdve与reject中的this为当 前创立的Promise;这样我们最最最基础的Promise就
13、完成了(只有头部没有四肢)Promise 高级- .then接着,所有的Promise实例都可以用.then方法,其中.then的两个参数,成功的回调 和失败的回调也就是我们所说的resolve和reject:fuACtio八 Proii(execo(tor) let fhis = this;_this.$ 轲 tatiAS = 3 Adi八g1;上hk.failCaMBack = a 八 def/认 cd;_tkiss(ACcessCallback = 八 defined;_tM/s.error = 八 de 6八 cd;execto%soe.bihd(J;k。句ect.bi八d(_丽M);
14、fmeti。八 resold params) if kis.辑status = pc八+八g) _this.$stat(A,= Success1_thissiACcessCallhack(parais)function reject(parais) if (_this.$statb(s =3八4Mg) fkis.$ status = aiT_this.failCall8ack(parais)八=fa八ctio八fail) thissucceCallhack = fullthis.failCallBack = fail;/测试代码八cw八比,。八(匕6,可)= 成功以 36?).tkc八s =
15、console.log(re讲一下这里:可以看到我们增加了 failCallBacksuccessCallback,用来储存我们在then中回调,刚才也说过,then中可传递一个成功和一个失败的回调,当P的状态变为resolve时执行成功回调,当P的状态变为reject或者出错时那么执行失败的回调, 但是具体执行结果的控制权没有在这里。但是我们知道一定会调用其中的一个。executor任务成功了肯定有成功后的结果,失败了我们肯定也拿到失败的原因。 所以我们可以通过params来传递这个结果或者error reason (当然这里的params也可以拆开赋给Promise实例)其实写到这里如果是
16、面试题,基本上是通 过了,也不会有人让你去完整地去实现error:用来存储,传递reject信息以及错误信息Promise 进阶我想我们最迷恋的应该就是Promise的链式调用吧,因为它的出现最最最大的意 义就是使我们的callback看起来不那么hell(因为我之前讲到了 async比它更直 接),那么为什么then能链式调用呢? then 一定返回了一个也具有then方法 的对象我想大家应该都能猜到.then返回的也一定是一个promise,那么这里会有一个有 趣的问题,就是.then中返回的到底是一个新promise的还是链式头部的调用 去? ? ? ? ?从代码上乍一看, 叶。飞上&协
17、0八(.).。圮儿(.)像是针对最初的Promise对象进行了一连串的方法链 调用。然而实际上不管是then还是catch方法调用,都返回了一个新的promise对 象。简单有力地证明一下var begMPHOMise = new ProH/usc(f八cti。八(resolve) 物0队(。););/4匕 tke八FVokisc = begi八ProMisc.thc八(fc(八cti。八(valued) co h so/eog(VMe);y)var catclProiise = theProvise.catch(fo(nctio (error) C。八5。/。,0匕匕。匕(。匕匕。匕););
18、c。八so/c/og(beginProMise != tkc八FVok/usc); / = truce。八so/eog(tkc八Pkok/sc /=ctckPKOMsc); = true显而易见promise返回的是一个新的而非调用者不过这样的话难度就来了,我们看下面代码:function begi八0 (return new Pro砥soe = setTeout(_ = resolvefirst1 , ZOOO)1)begi八0ke八(d= coirisole.log(datd)return new Proiise(resolve = !).以。八(匕5 = co 八 so/e./og(re
19、s)1);我们知道最后的then中函数参数永远都不会执行,为什么说它难呢,想一下,之所 以能链式调用是因为.then。执行之后返回了一个新的promise,一定注意,我说的 新的promise是then。所返回而不是data = return new Promise.(这只是 then的一个参数),这样问题就来了,我们从刚才的情况看,知道只有第一个.then 中的状态改变时第二个then中的函数参数才会执行,放到程序上说也就是需要第 一个.then中返回的promise状态改变!即:begiiQ).tlei(data = coisole.log(datd)return new PKOMsc(r
20、csoHe = s况丁丽= 丫esolveCtwo), 2。)!).以0八(匕6 = co 八 so/eog(Ves)直接从代码的角度上讲,调用了第一个.then中的函数参数中的resolve之后第一个.then()返回的promise状态也改变了,这句话有些绕,我用一张图来讲:第一个thsn返回了一个新的promisebegin(J.thenfdato -第fthen.then的多数函数又返回了一个新的这里调用resolve之后相应的外部的pro 也改变了);return new Proise(resolve - setTimeoutQ - resol1000) 我们把.then返回的叫做P
21、I, .then的学数区 叫做P2流程如下:P2: pending - resolveP1: pending - resolvePromise之间存在联第二个.th!中的参政函数执行那么问题就来了,我们如何使得P2的状态发生改变通知P1?其实这里用观察者模式是可以的,但是代价有点大,换个角度想,其实我们直接让P2中的resolve等于P1中的resolve不就可以了?这样P2中调用了 resolve之后同步的P1也相当于onresolve上代码:fiA八ction PKOki(exccator) let fhis = this;_this .押 tatus =,pe 八fhis.failCal
22、lBack 二八defined;J;lis(ACceCallhack = tmdcfi八cd;丫 csbdt = 八 defined;Jjhis.e。丫 =认八 defined;setTiveob(t(_ = exec(Ato_this.resolve.bi八_jthisYeject,bMd(_this);1)P丫。Mi.pYotatgpc.the八=f八ctio八fail) let ncwFVo=八cw PhomQ = );tlissiACcessCallback = full;this.failCallBack = fail;this.succcssDefw =八 cwPKOg.KCSoH
23、c.bMW(八 cwPkom);加s.failDefw = iewProii.reject.biid(iewProii); return MwProvxi);Pirogi.prototype.resolve =心八cti。八let fhis = this;if=,pc八ding) = KaccesU;if (IjthissuccessCallback return;let result = fhis.SnccessCaMback(pa%i*s);if (result & result Msta八ccof Phom。res”化.the八(_tk.s“ccessDcfcr, _this.fai(D
24、efcC; return ”)_tkissaccessDcfcK(KCsa/t)Pirogi.prototype.reject = 八田0Mp4K4Ms) let fhis = this;if 轲七=,pc八dMg) _this.$status = fail1)if (!_this.failCal(Back) return;let iresult = _%is.fai(CaHBack(pakaMS);if (rescdt & result Msta八ceof Pirogi) res”化the八(_tk.s“cccssDcfc匕,his.fai(Dcfe吟;retam 11)_tkis.SccessDcfcK(KCS/t)/测试代码八cw FVomi(f八cti。八(Kes)可)setTiieoiAt(_ = kcs,成功)150。).tkc八(r*es = co 八 so/c/og(rcs);return 第一个.the八成功,).tkc八cs = co h so/c./og(res);