带你写出符合Promise/A+ 规范Promise的源码

Promise是前端面试中的高频问题,如果你能根据PromiseA+的规范,写出符合规范的源码,那么我想,对于面试中的Promise相关的问题,都能够给出比较完美的答案。 2020-07-29 17:35:08 Promise源码前端 PC人脸识别登录,出乎意料的简单 之前不是做了个开源项目嘛,在做完GitHub登录后,想着再显得有逼格一点,说要再加个人脸识别登录,就我这佛系的开发进度,过了一周总算是抽时间安排上了。 2020-07-29 17:15:19 Java人脸识别人工智能 通过VSCode RTOS插件使用Python为物联网系统编写程序 RTOS 之类的实时嵌入式操作系统使嵌入式系统的编程更简单。物联网(IoT)无处不在,它意味着几乎所有的产品,从冰箱到口袋手表,都可以连网。为了实现这个目标,每个产品都需要拥有一个嵌入式计算机来运行网络栈,有些产品几乎是小的不可思议。 2020-07-29 17:01:29 VSCode RTOSPython编程 分分钟甩Word几条街,Python编辑公式竟可以如此简单 我们在 Word 中编辑文本时,遇到超复杂的公式,想想就令人头大,一个不小心就会输错。真心不想用啊,写论文就够令人头疼了,没想到,最难的是编辑超长的公式。

Promise前端面试中的高频问题,如果你能根据PromiseA+的规范,写出符合规范的源码,那么我想,对于面试中的Promise相关的问题,都能够给出比较完美的答案。

Promise是前端面试中的高频问题,如果你能根据PromiseA+的规范,写出符合规范的源码,那么我想,对于面试中的Promise相关的问题,都能够给出比较完美的答案。

我的建议是,对照规范多写几次实现,也许第一遍的时候,是改了多次,才能通过测试,那么需要反复的写,我已经将Promise的源码实现写了不下七遍,不那么聪明的话,当然需要更加努力啦~

[[335576]]

Promise的源码实现

  1. /**
  2. *1.newPromise时,需要传递一个executor执行器,执行器立刻执行
  3. *2.executor接受两个参数,分别是resolve和reject
  4. *3.promise只能从pending到rejected,或者从pending到fulfilled
  5. *4.promise的状态一旦确认,就不会再改变
  6. *5.promise都有then方法,then接收两个参数,分别是promise成功的回调onFulfilled,
  7. *和promise失败的回调onRejected
  8. *6.如果调用then时,promise已经成功,则执行onFulfilled,并将promise的值作为参数传递进去。
  9. *如果promise已经失败,那么执行onRejected,并将promise失败的原因作为参数传递进去。
  10. *如果promise的状态是pending,需要将onFulfilled和onRejected函数存放起来,等待状态确定后,再依次将对应的函数执行(发布订阅)
  11. *7.then的参数onFulfilled和onRejected可以缺省
  12. *8.promise可以then多次,promise的then方法返回一个promise
  13. *9.如果then返回的是一个结果,那么就会把这个结果作为参数,传递给下一个then的成功的回调(onFulfilled)
  14. *10.如果then中抛出了异常,那么就会把这个异常作为参数,传递给下一个then的失败的回调(onRejected)
  15. *11.如果then返回的是一个promise,那么会等这个promise执行完,promise如果成功,
  16. *就走下一个then的成功,如果失败,就走下一个then的失败
  17. */
  18. constPENDING='pending';
  19. constFULFILLED='fulfilled';
  20. constREJECTED='rejected';
  21. functionPromise(executor){
  22. letself=this;
  23. self.status=PENDING;
  24. self.onFulfilled=[];//成功的回调
  25. self.onRejected=[];//失败的回调
  26. //PromiseA+2.1
  27. functionresolve(value){
  28. if(self.status===PENDING){
  29. self.status=FULFILLED;
  30. self.value=value;
  31. self.onFulfilled.forEach(fn=>fn());//PromiseA+2.2.6.1
  32. }
  33. }
  34. functionreject(reason){
  35. if(self.status===PENDING){
  36. self.status=REJECTED;
  37. self.reason=reason;
  38. self.onRejected.forEach(fn=>fn());//PromiseA+2.2.6.2
  39. }
  40. }
  41. try{
  42. executor(resolve,reject);
  43. }catch(e){
  44. reject(e);
  45. }
  46. }
  47. Promise.prototype.then=function(onFulfilled,onRejected){
  48. //PromiseA+2.2.1/PromiseA+2.2.5/PromiseA+2.2.7.3/PromiseA+2.2.7.4
  49. onFulfilled=typeofonFulfilled==='function'?onFulfilled:value=>value;
  50. onRejected=typeofonRejected==='function'?onRejected:reason=>{throwreason};
  51. letself=this;
  52. //PromiseA+2.2.7
  53. letpromise2=newPromise((resolve,reject)=>{
  54. if(self.status===FULFILLED){
  55. //PromiseA+2.2.2
  56. //PromiseA+2.2.4---setTimeout
  57. setTimeout(()=>{
  58. try{
  59. //PromiseA+2.2.7.1
  60. letx=onFulfilled(self.value);
  61. resolvePromise(promise2,x,resolve,reject);
  62. }catch(e){
  63. //PromiseA+2.2.7.2
  64. reject(e);
  65. }
  66. });
  67. }elseif(self.status===REJECTED){
  68. //PromiseA+2.2.3
  69. setTimeout(()=>{
  70. try{
  71. letx=onRejected(self.reason);
  72. resolvePromise(promise2,x,resolve,reject);
  73. }catch(e){
  74. reject(e);
  75. }
  76. });
  77. }elseif(self.status===PENDING){
  78. self.onFulfilled.push(()=>{
  79. setTimeout(()=>{
  80. try{
  81. letx=onFulfilled(self.value);
  82. resolvePromise(promise2,x,resolve,reject);
  83. }catch(e){
  84. reject(e);
  85. }
  86. });
  87. });
  88. self.onRejected.push(()=>{
  89. setTimeout(()=>{
  90. try{
  91. letx=onRejected(self.reason);
  92. resolvePromise(promise2,x,resolve,reject);
  93. }catch(e){
  94. reject(e);
  95. }
  96. });
  97. });
  98. }
  99. });
  100. returnpromise2;
  101. }
  102. functionresolvePromise(promise2,x,resolve,reject){
  103. letself=this;
  104. //PromiseA+2.3.1
  105. if(promise2===x){
  106. reject(newTypeError('Chainingcycle'));
  107. }
  108. if(x&&typeofx==='object'||typeofx==='function'){
  109. letused;//PromiseA+2.3.3.3.3只能调用一次
  110. try{
  111. letthen=x.then;
  112. if(typeofthen==='function'){
  113. //PromiseA+2.3.3
  114. then.call(x,(y)=>{
  115. //PromiseA+2.3.3.1
  116. if(used)return;
  117. used=true;
  118. resolvePromise(promise2,y,resolve,reject);
  119. },(r)=>{
  120. //PromiseA+2.3.3.2
  121. if(used)return;
  122. used=true;
  123. reject(r);
  124. });
  125. }else{
  126. //PromiseA+2.3.3.4
  127. if(used)return;
  128. used=true;
  129. resolve(x);
  130. }
  131. }catch(e){
  132. //PromiseA+2.3.3.2
  133. if(used)return;
  134. used=true;
  135. reject(e);
  136. }
  137. }else{
  138. //PromiseA+2.3.3.4
  139. resolve(x);
  140. }
  141. }
  142. module.exports=Promise;

有专门的测试脚本可以测试所编写的代码是否符合PromiseA+的规范。

首先,在promise实现的代码中,增加以下代码:

  1. PromisePromise.defer=Promise.deferred=function(){
  2. letdfd={};
  3. dfd.promise=newPromise((resolve,reject)=>{
  4. dfd.resolve=resolve;
  5. dfd.reject=reject;
  6. });
  7. returndfd;
  8. }

安装测试脚本:

  1. npminstall-gpromises-aplus-tests

如果当前的promise源码的文件名为promise.js

那么在对应的目录执行以下命令:

  1. promises-aplus-testspromise.js

promises-aplus-tests中共有872条测试用例。以上代码,可以完美通过所有用例。

对上面的代码实现做一点简要说明(其它一些内容注释中已经写得很清楚):

  1. onFulfilled 和 onFulfilled的调用需要放在setTimeout,因为规范中表示: onFulfilled or onRejected must not be called until the execution context stack contains only platform code。使用setTimeout只是模拟异步,原生Promise并非是这样实现的。

2. 在 resolvePromise 的函数中,为何需要usedd这个flag,同样是因为规范中明确表示: If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored. 因此我们需要这样的flag来确保只会执行一次。

3. self.onFulfilled 和 self.onRejected 中存储了成功的回调和失败的回调,根据规范2.6显示,当promise从pending态改变的时候,需要按照顺序去指定then对应的回调。

PromiseA+的规范(翻译版)

PS: 下面是我翻译的规范,供参考

术语

  1. promise 是一个有then方法的对象或者是函数,行为遵循本规范
  2. thenable 是一个有then方法的对象或者是函数
  3. value 是promise状态成功时的值,包括 undefined/thenable或者是 promise
  4. exception 是一个使用throw抛出的异常值
  5. reason 是promise状态失败时的值

要求

2.1 Promise States

Promise 必须处于以下三个状态之一: pending, fulfilled 或者是 rejected

2.1.1 如果promise在pending状态

  1. 2.1.1.1可以变成fulfilled或者是rejected

2.1.2 如果promise在fulfilled状态

  1. 2.1.2.1不会变成其它状态
  2. 2.1.2.2必须有一个value值

2.1.3 如果promise在rejected状态

  1. 2.1.3.1不会变成其它状态
  2. 2.1.3.2必须有一个promise被reject的reason

概括即是:promise的状态只能从pending变成fulfilled,或者从pending变成rejected.promise成功,有成功的value.promise失败的话,有失败的原因

2.2 then方法

promise必须提供一个then方法,来访问最终的结果

promise的then方法接收两个参数

  1. promise.then(onFulfilled,onRejected)

2.2.1 onFulfilled 和 onRejected 都是可选参数

  1. 2.2.1.1onFulfilled必须是函数类型
  2. 2.2.1.2onRejected必须是函数类型

2.2.2 如果 onFulfilled 是函数:

  1. 2.2.2.1必须在promise变成fulfilled时,调用onFulfilled,参数是promise的value
  2. 2.2.2.2在promise的状态不是fulfilled之前,不能调用
  3. 2.2.2.3onFulfilled只能被调用一次

2.2.3 如果 onRejected 是函数:

  1. 2.2.3.1必须在promise变成rejected时,调用onRejected,参数是promise的reason
  2. 2.2.3.2在promise的状态不是rejected之前,不能调用
  3. 2.2.3.3onRejected只能被调用一次

2.2.4 onFulfilled 和 onRejected 应该是微任务

2.2.5 onFulfilled 和 onRejected 必须作为函数被调用

2.2.6 then方法可能被多次调用

  1. 2.2.6.1如果promise变成了fulfilled态,所有的onFulfilled回调都需要按照then的顺序执行
  2. 2.2.6.2如果promise变成了rejected态,所有的onRejected回调都需要按照then的顺序执行

2.2.7 then必须返回一个promise

  1. promise2=promise1.then(onFulfilled,onRejected);
  2. 2.2.7.1onFulfilled或onRejected执行的结果为x,调用resolvePromise
  3. 2.2.7.2如果onFulfilled或者onRejected执行时抛出异常e,promise2需要被reject
  4. 2.2.7.3如果onFulfilled不是一个函数,promise2以promise1的值fulfilled
  5. 2.2.7.4如果onRejected不是一个函数,promise2以promise1的reasonrejected

2.3 resolvePromise

resolvePromise(promise2, x, resolve, reject)

2.3.1 如果 promise2 和 x 相等,那么 reject promise with a TypeError

2.3.2 如果 x 是一个 promsie

  1. 2.3.2.1如果x是pending态,那么promise必须要在pending,直到x变成fulfilledorrejected.
  2. 2.3.2.2如果x被fulfilled,fulfillpromisewiththesamevalue.
  3. 2.3.2.3如果x被rejected,rejectpromisewiththesamereason.

2.3.3 如果 x 是一个 object 或者 是一个 function

  1. 2.3.3.1letthen=x.then.
  2. 2.3.3.2如果x.then这步出错,那么rejectpromisewitheasthereason..
  3. 2.3.3.3如果then是一个函数,then.call(x,resolvePromiseFn,rejectPromise)
  4. 2.3.3.3.1resolvePromiseFn的入参是y,执行resolvePromise(promise2,y,resolve,reject);
  5. 2.3.3.3.2rejectPromise的入参是r,rejectpromisewithr.
  6. 2.3.3.3.3如果resolvePromise和rejectPromise都调用了,那么第一个调用优先,后面的调用忽略。
  7. 2.3.3.3.4如果调用then抛出异常e
  8. 2.3.3.3.4.1如果resolvePromise或rejectPromise已经被调用,那么忽略
  9. 2.3.3.3.4.3否则,rejectpromisewitheasthereason
  10. 2.3.3.4如果then不是一个function.fulfillpromisewithx.

2.3.4 如果 x 不是一个 object 或者 function,fulfill promise with x.

Promise的其他方法

虽然上述的promise源码已经符合PromiseA+的规范,但是原生的Promise还提供了一些其他方法,如:

  1. Promise.resolve()
  2. Promise.reject()
  3. Promise.prototype.catch()
  4. Promise.prototype.finally()
  5. Promise.all()
  6. Promise.race()

下面具体说一下每个方法的实现:

Promise.resolve

Promise.resolve(value) 返回一个以给定值解析后的Promise 对象.

  1. 如果 value 是个 thenable 对象,返回的promise会“跟随”这个thenable的对象,采用它的最终状态
  2. 如果传入的value本身就是promise对象,那么Promise.resolve将不做任何修改、原封不动地返回这个promise对象。
  3. 其他情况,直接返回以该值为成功状态的promise对象。
  1. Promise.resolve=function(param){
  2. if(paraminstanceofPromise){
  3. returnparam;
  4. }
  5. returnnewPromise((resolve,reject)=>{
  6. if(param&&typeofparam==='object'&&typeofparam.then==='function'){
  7. setTimeout(()=>{
  8. param.then(resolve,reject);
  9. });
  10. }else{
  11. resolve(param);
  12. }
  13. });
  14. }

thenable对象的执行加 setTimeout的原因是根据原生Promise对象执行的结果推断的,如下的测试代码,原生的执行结果为: 20 400 30;为了同样的执行顺序,增加了setTimeout延时。

测试代码:

  1. letp=Promise.resolve(20);
  2. p.then((data)=>{
  3. console.log(data);
  4. });
  5. letp2=Promise.resolve({
  6. then:function(resolve,reject){
  7. resolve(30);
  8. }
  9. });
  10. p2.then((data)=>{
  11. console.log(data)
  12. });
  13. letp3=Promise.resolve(newPromise((resolve,reject)=>{
  14. resolve(400)
  15. }));
  16. p3.then((data)=>{
  17. console.log(data)
  18. });

Promise.reject

Promise.reject方法和Promise.resolve不同,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。

  1. Promise.reject=function(reason){
  2. returnnewPromise((resolve,reject)=>{
  3. reject(reason);
  4. });
  5. }

Promise.prototype.catch

Promise.prototype.catch 用于指定出错时的回调,是特殊的then方法,catch之后,可以继续 .then

  1. Promise.prototype.catch=function(onRejected){
  2. returnthis.then(null,onRejected);
  3. }

Promise.prototype.finally

不管成功还是失败,都会走到finally中,并且finally之后,还可以继续then。并且会将值原封不动的传递给后面的then.

  1. Promise.prototype.finally=function(callback){
  2. returnthis.then((value)=>{
  3. returnPromise.resolve(callback()).then(()=>{
  4. returnvalue;
  5. });
  6. },(err)=>{
  7. returnPromise.resolve(callback()).then(()=>{
  8. throwerr;
  9. });
  10. });
  11. }

Promise.all

Promise.all(promises) 返回一个promise对象

  1. 如果传入的参数是一个空的可迭代对象,那么此promise对象回调完成(resolve),只有此情况,是同步执行的,其它都是异步返回的。
  2. 如果传入的参数不包含任何 promise,则返回一个异步完成.
  3. promises 中所有的promise都promise都“完成”时或参数中不包含 promise 时回调完成。
  4. 如果参数中有一个promise失败,那么Promise.all返回的promise对象失败
  5. 在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组
  1. Promise.all=function(promises){
  2. promises=Array.from(promises);//将可迭代对象转换为数组
  3. returnnewPromise((resolve,reject)=>{
  4. letindex=0;
  5. letresult=[];
  6. if(promises.length===0){
  7. resolve(result);
  8. }else{
  9. functionprocessValue(i,data){
  10. result[i]=data;
  11. if(++index===promises.length){
  12. resolve(result);
  13. }
  14. }
  15. for(leti=0;i<promises.length;i++){
  16. //promises[i]可能是普通值
  17. Promise.resolve(promises[i]).then((data)=>{
  18. processValue(i,data);
  19. },(err)=>{
  20. reject(err);
  21. return;
  22. });
  23. }
  24. }
  25. });
  26. }

测试代码:

  1. varpromise1=newPromise((resolve,reject)=>{
  2. resolve(3);
  3. })
  4. varpromise2=42;
  5. varpromise3=newPromise(function(resolve,reject){
  6. setTimeout(resolve,100,'foo');
  7. });
  8. Promise.all([promise1,promise2,promise3]).then(function(values){
  9. console.log(values);//[3,42,'foo']
  10. },(err)=>{
  11. console.log(err)
  12. });
  13. varp=Promise.all([]);//willbeimmediatelyresolved
  14. varp2=Promise.all([1337,"hi"]);//non-promisevalueswillbeignored,buttheevaluationwillbedoneasynchronously
  15. console.log(p);
  16. console.log(p2)
  17. setTimeout(function(){
  18. console.log('thestackisnowempty');
  19. console.log(p2);
  20. });

Promise.race

Promise.race函数返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。

如果传的参数数组是空,则返回的 promise 将永远等待。

如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,则 Promise.race 将解析为迭代中找到的第一个值。

  1. Promise.race=function(promises){
  2. promises=Array.from(promises);//将可迭代对象转换为数组
  3. returnnewPromise((resolve,reject)=>{
  4. if(promises.length===0){
  5. return;
  6. }else{
  7. for(leti=0;i<promises.length;i++){
  8. Promise.resolve(promises[i]).then((data)=>{
  9. resolve(data);
  10. return;
  11. },(err)=>{
  12. reject(err);
  13. return;
  14. });
  15. }
  16. }
  17. });
  18. }

测试代码:

  1. Promise.race([
  2. newPromise((resolve,reject)=>{setTimeout(()=>{resolve(100)},1000)}),
  3. undefined,
  4. newPromise((resolve,reject)=>{setTimeout(()=>{reject(100)},100)})
  5. ]).then((data)=>{
  6. console.log('success',data);
  7. },(err)=>{
  8. console.log('err',err);
  9. });
  10. Promise.race([
  11. newPromise((resolve,reject)=>{setTimeout(()=>{resolve(100)},1000)}),
  12. newPromise((resolve,reject)=>{setTimeout(()=>{resolve(200)},200)}),
  13. newPromise((resolve,reject)=>{setTimeout(()=>{reject(100)},100)})
  14. ]).then((data)=>{
  15. console.log(data);
  16. },(err)=>{
  17. console.log(err);
  18. });

©本文为清一色官方代发,观点仅代表作者本人,与清一色无关。清一色对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文不作为投资理财建议,请读者仅作参考,并请自行承担全部责任。文中部分文字/图片/视频/音频等来源于网络,如侵犯到著作权人的权利,请与我们联系(微信/QQ:1074760229)。转载请注明出处:清一色财经

(0)
打赏 微信扫码打赏 微信扫码打赏 支付宝扫码打赏 支付宝扫码打赏
清一色的头像清一色管理团队
上一篇 2023年5月5日 12:26
下一篇 2023年5月5日 12:26

相关推荐

发表评论

登录后才能评论

联系我们

在线咨询:1643011589-QQbutton

手机:13798586780

QQ/微信:1074760229

QQ群:551893940

工作时间:工作日9:00-18:00,节假日休息

关注微信