好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

ES6+ 实现一个简版的 Promise

ES6+ 实现 一个 简版的 Promise

1. 前言

上一节我们学习了 ES6 Promise的基本 用法 ,并且我们知道 Promise 最早出现在社区,所以ES6 中 Promise 也是遵循 一个 标准的规范的。这个规范就是 Promise A+ 规范 也就是任何人都可以遵循这个规范实现 一个 自己的 Promise,由于每个人实现的方式有所差异, Promise A+ 规范 给出了一些要求和兼容方式。

本节我们将根据 Promise A+ 规范 实现 一个 简版的 Promise API。

2. 实现步骤

上一节我们已经知道了 Promise 是 一个 类, 默 认接收 一个 参数 executor(执行器),并且会立即执行。所以首先需要创建 一个 Promise 的类,然后传入 一个 回调 函数 并执行它,故有如下的初始 代码 :

  class   Promise   { 
   constructor  ( executor )   { 
     executor  (  )  ; 
   } 
 } 
 

Promise 有三个状态:等待(padding)、成功(fulfilled),失败(rejected)。 默 认是等待状态,等待态可以突变为成功态或失败态,所以我们可以定义三个常量来存放这三个状态

  const  PENDING  =   'PENDING'  ; 
 const  RESOLVED  =   'RESOLVED'  ; 	 // 成功态 
 const  REJECTED  =   'REJECTED'  ; 	 // 失败态 
 class   Promise   { 
   constructor  ( executor )   { 
     this  . status  =  PENDING ; 	 //  默 认是等待态 
     executor  (  )  ; 
   } 
 } 
 

这样我们就知道了 Promise 的基本状态,那内部的状态是怎么突变为成功或失败的呢?这里执行器(executor)会提供两个个 方法 用于改变 Promise 的状态,所以我们需要在初始化时定义 resolve 和 reject 方法 :在成功的时候会传入成功的值,在失败的时候会传入失败的原因。并且每个Promise 都会提供 then 方法 用于链式 调用 。

  class   Promise   { 
   constructor  ( executor )   { 
     this  . status  =  PENDING ; 
     const   resolve   =   ( value )   =>   {  }  ; 
     const   reject   =   ( reason )   =>   {  }  ; 
     // 执行executor时,传入成功或失败的回调 
     executor  ( resolve ,  reject )  ; 
   } 
   then  ( onfulfilled ,  onrejected )   { 
    
   } 
 } 
 

这时我们就可以开始着手去更改 Promise的状态了,由于 默 认情况下 Promise 的状态只能从 pending 到 fulfilled 和 rejected 的转化。

  class   Promise   { 
   constructor  ( executor )   { 
     this  . status  =  PENDING ; 
     const   resolve   =   ( value )   =>   { 
       // 只有等待态时才能更改状态 
       if   (  this  . status  ===  PENDING )   { 
         this  . status  =  RESOLVED ; 
       } 
     }  ; 
     const   reject   =   ( reason )   =>   { 
       if   (  this  . status  ===  PENDING )   { 
         this  . status  =  REJECTED ; 
       } 
     }  ; 
     executor  ( resolve ,  reject )  ; 
   } 
   ... 
 } 
 

成功和失败都会返回对应的结果,所以我们需要定义成功的值和失败的原因两个 全局变量 ,用于存放返回的结果。

  class   Promise   { 
   constructor  ( executor )   { 
     this  . status  =  PENDING ; 
     this  . value  =  undefined ; 
     this  . reason  =  undefined ; 
     const   resolve   =   ( value )   =>   { 
       // 只有等待态时才能更改状态 
       if   (  this  . status  ===  PENDING )   { 
         this  . value  =  value ; 
         this  . status  =  RESOLVED ; 
       } 
     }  ; 
     const   reject   =   ( reason )   =>   { 
       if   (  this  . status  ===  PENDING )   { 
         this  . reason  =  reason ; 
         this  . status  =  REJECTED ; 
       } 
     }  ; 
     executor  ( resolve ,  reject )  ; 
   } 
   ... 
 } 
 

这时我们就已经为执行器提供了两个回调 函数 了,如果在执行器执行时抛出异常时,我们需要使用 try…catch 来补货一下。由于是抛出异常,所以,需要 调用 reject 方法 来 修改 为失败的状态。

  try   { 
   executor  ( resolve ,  reject )  ; 
 }   catch  ( e )   { 
   reject  ( e ) 
 } 
 

我们知道实例在 调用 then 方法 时会传入两个回调 函数 onfulfilled, onrejected 去执行成功或失败的回调,所以根据状态会 调用 对应的 函数 来处理。

  then  ( onfulfilled ,  onrejected )   { 
   if   (  this  . status  ===  RESOLVED )   { 
     onfulfilled  (  this  . value ) 
   } 
   if   (  this  . status  ===  REJECTED )   { 
     onrejected  (  this  . reason ) 
   } 
 } 
 

这样我们就完了 Promise 最基本的同步 功能 ,

  let  promise  =   new   Promise  (  ( resolve ,  reject )   =>   { 
   resolve  (  'value'  )  ; 
   // throw new Error(' 错误 '); 
   // reject('error reason') 
   // setTimeout(() => { 
   //   resolve('value'); 
   // }, 1000) 
 }  ) 
promise .  then  (  ( data )   =>   { 
  console .  log  (  'resolve response'  ,  data )  ; 
 }  ,   ( err )   =>   { 
  console .  log  (  'reject response'  ,  err )  ; 
 }  ) 
 

用上面的 代码 对我们写的 Promise 进行验证,通过测试用例可知,我们写的 Promise 只能在同步中运行,当我们使用 setTimeout 异步去返回时,并没有预想的在then的成功回调中打印结果。

对于这种异步行为需要专门处理,如何处理异步的 内容 呢?我们知道在执行异步任务时 Promise 的状态并没有被改变,也就是并没有执行 resolve 或 reject 方法 ,但是 then 中的回调已经执行了,这时就需要 增加 当 Promise 还是等待态的逻辑,在等待态时把回调 函数 都存放起来,等到执行 resolve 或 reject 再依次执行之前存放的then的回调 函数 ,也就是我们平时用到的发布 订阅 模式。实现步骤:

首先,需要在初始化中 增加 存放成功的回调 函数 和存放失败的回调 函数 ; 然后,由于是异步执行 resolve 或 reject 所以需要在 then 方法 中把回调 函数 存放起来; 最后,当执行 resolve 或 reject 时取出存放的回调 函数 依次执行。

根据以上的实现步骤可以得到如下的 代码 :

  class   Promise   { 
   constructor  ( executor )   { 
	 this  . status  =  PENDING ; 
     this  . value  =  undefined ;   // 成功的值 
     this  . reason  =  undefined ;   // 失败的原因 
 +     // 存放成功的回调 函数  
 +      this  . onResolvedCallbacks  =   [  ]  ; 
 +      // 存放失败的回调 函数  
 +      this  . onRejectedCallbacks  =   [  ]  ; 
     let   resolve   =   ( value )   =>   { 
       if   (  this  . status  ===  PENDING )   { 
		 this  . value  =  value ; 
         this  . status  =  RESOLVED ; 
 +         // 异步时,存放在成功的回调 函数 依次执行 
 +         this  . onResolvedCallbacks .  forEach  ( fn  =>   fn  (  )  ) 
       } 
     }  ; 
     let   reject   =   ( reason )   =>   { 
       if   (  this  . status  ===  PENDING )   { 
		 this  . value  =  reason ; 
         this  . status  =  REJECTED ; 
 +         // 异步时,存放在失败的回调 函数 依次执行 
 +         this  . onRejectedCallbacks .  forEach  ( fn  =>   fn  (  )  ) 
       } 
     }  ; 
     try   { 
       executor  ( resolve ,  reject )  ; 
     }   catch  ( e )   { 
       reject  ( e ) 
     } 
   } 
   then  ( onfulfilled ,  onrejected )   { 
     if   (  this  . status  ===  RESOLVED )   { 
       onfulfilled  (  this  . value ) 
     } 
     if   (  this  . status  ===  REJECTED )   { 
       onrejected  (  this  . reason ) 
     } 
 +      if   (  this  . status  ===  PENDING )   { 
 +        this  . onResolvedCallbacks .  push  (  (  )   =>   { 
 +          // T odo  
 +          onfulfilled  (  this  . value )  ; 
 +        }  ) 
 +        this  . onRejectedCallbacks .  push  (  (  )   =>   { 
 +          // T odo  
 +          onrejected  (  this  . reason )  ; 
 +        }  ) 
 +      } 
   } 
 } 
 

上面的 代码 中,在存放回调 函数 时把 onfulfilled , onrejected 存放在 一个 函数 中执行,这样的好处是可以在前面 增加 处理问题的逻辑。这样我们就完成了处理异步的 Promise 逻辑。下面是测试用例,可以正常的执行 then 的成功回调 函数 。

  let  promise  =   new   Promise  (  ( resolve ,  reject )   =>   { 
   setTimeout  (  (  )   =>   { 
     resolve  (  '100'  )  ; 
   }  ,   ) 
 }  ) 
promise .  then  (  ( data )   =>   { 
  console .  log  (  'resolve response:'  ,  data )  ;   // resolve response: 100 
 }  ,   ( err )   =>   { 
  console .  log  (  'reject response:'  ,  err )  ; 
 }  ) 
 

到这里我们是不是已经基本实现了 Promise 的 功能 呢?ES6 中的 then 方法 支持 链式 调用 ,那我们写的可以吗?我们在看下面的 一个 测试用例:

  let  promise  =   new   Promise  (  ( resolve ,  reject )   =>   { 
   setTimeout  (  (  )   =>   { 
     resolve  (  '100'  )  ; 
   }  ,   ) 
 }  ) 
promise .  then  (  ( data )   =>   { 
  console .  log  (  'resolve response:'  ,  data )  ;   // resolve response: 100 
   return  
 }  ,   ( err )   =>   { 
  console .  log  (  'reject response:'  ,  err )  ; 
 }  )  .  then  (  ( data )   =>   { 
  console .  log  (  'data2:'  ,  data ) 
 }  ,   null  ) 
 // TypeError: Cannot read property 'then' of undefined 
 

然而当我们在执行的时候会报错,then 是 undefined。为什么会这样呢?那我们要知道如何满足链式 调用 的规范,那就是在完成任务后再返回 一个 Promise 实例。那如何返回 一个 Promise 实例呢?在 Promise A+ 规范的 2.2.7 小节在有详细的描述,再实例化 一个 promise2 来存放执行后的结果,并返回 promise2。那么我们就要改造 then 方法 了。

  class   Promise   { 
   ... 
   then  ( onfulfilled ,  onrejected )   { 
	 let  promise2  =   new   Promise  (  ( resolve ,  reject )   =>   { 
       if   (  this  . status  ===  RESOLVED )   { 
         const  x  =   onfulfilled  (  this  . value ) 
		 resolve  ( x ) 
       } 
       if   (  this  . status  ===  REJECTED )   { 
         const  x  =   onrejected  (  this  . reason )  ; 
         reject  ( x ) 
       } 
       if   (  this  . status  ===  PENDING )   { 
         this  . onResolvedCallbacks .  push  (  (  )   =>   { 
	         const  x  =   onfulfilled  (  this  . value ) 
			 resolve  ( x ) 
         }  ) 
         this  . onRejectedCallbacks .  push  (  (  )   =>   { 
	         const  x  =   onrejected  (  this  . reason )  ; 
        	 reject  ( x ) 
         }  ) 
       } 
     }  ) 

     return  promise2
   } 
 } 
 

再使用上面的测试用例,就可以得到正确的结果:

  let  promise  =   new   Promise  (  ( resolve ,  reject )   =>   { 
   resolve  (  '100'  )  ; 
 }  ) 
promise .  then  (  ( data )   =>   { 
  console .  log  (  'data1:'  ,  data )  ; 	 // data1: 100 
   return  
 }  ,   null  )  .  then  (  ( data )   =>   { 
  console .  log  (  'data2:'  ,  data )  ; 	 // data2: 200 
   throw   new   Error  (  'error'  ) 
 }  ,   null  )  .  then  (  null  ,   (  )   =>   { 
  consol .  log  (  '程序报错...'  ) 
 }  ) 
 

上面的测试用例中,当 then 的回调 函数 抛出异常时需要去捕获 错误 ,传到下 一个 then 的失败回调 函数 中。

  class   Promise   { 
   ... 
   then  ( onfulfilled ,  onrejected )   { 
		 let  promise2  =   new   Promise  (  ( resolve ,  reject )   =>   { 
       if   (  this  . status  ===  RESOLVED )   { 
         try  { 
			 const  x  =   onfulfilled  (  this  . value ) 
			 resolve  ( x ) 
         }   catch  ( e )   { 
			 reject  ( e ) 
         } 
       } 
       if   (  this  . status  ===  REJECTED )   { 
         try  { 
			 const  x  =   onrejected  (  this  . reason )  ; 
        	 resolve  ( x ) 
         }   catch  ( e )   { 
			 reject  ( e ) 
         } 
       } 
       if   (  this  . status  ===  PENDING )   { 
         this  . onResolvedCallbacks .  push  (  (  )   =>   { 
           try  { 
             const  x  =   onfulfilled  (  this  . value ) 
             resolve  ( x ) 
           }   catch  ( e )   { 
             reject  ( e ) 
           } 
         }  ) 
         this  . onRejectedCallbacks .  push  (  (  )   =>   { 
           try  { 
             const  x  =   onrejected  (  this  . reason )  ; 
             resolve  ( x ) 
           }   catch  ( e )   { 
             reject  ( e ) 
           } 
         }  ) 
       } 
     }  ) 
     return  promise2
   } 
 } 
 

到这里为止我们就已经实现了 一个 简版的 Promise,因为Promise是 一个 规范,很多人都可以实现自己的 Promise 所以 Promise A+ 规范做了很多兼容处理的要求,如果想实现 一个 完整的 Promise 可以参考 Promise A+ 规范 。

3. 小结

本节主要按照 Promise A+ 规范 部分的要求实现了 一个 简版的 Promise API 这个 Promise 基本上满足同步异步的链式 调用 ,对基本的异常做了处理。当然 Promise A+ 规范 所规定的细节比较多,剩下的都是对各种异常 错误 的处理,所以后面我们也没有去实现。另外官网下提供了 一个 测试用例来验证我们写的 Promise 是否符合 Promise A+ 规范 ,所以可以参考 promises-tests 这个库来完成我们的 Promise 的测试。

【译】 Promises/A+ 规范

查看更多关于ES6+ 实现一个简版的 Promise的详细内容...

  阅读:43次

上一篇

下一篇

第1节:ES6+ 简介    第2节:ES6 环境配置    第3节:ES6+ let    第4节:ES6+ const    第5节:ES6+ 展开语法    第6节:ES6+ 剩余参数    第7节:ES6+ 解构赋值    第8节:ES6+ 模版字符串    第9节:ES6+ 箭头函数    第10节:ES6+ 数值扩展    第11节:ES6+ isFinite()&isNaN()    第12节:ES6+ Number 对象的方法    第13节:ES6+ Math 对象的扩展    第14节:ES6+ includes()    第15节:ES6+ 字符串的扩展    第16节:ES6+ startsWith()    第17节:ES6+ endsWith()    第18节:ES6+ repeat()    第19节:ES6+ padStart()    第20节:ES6+ padEnd()    第21节:ES6+ trim()    第22节:ES6+ Array.from()    第23节:ES6+ of()    第24节:ES6+ find()和findIndex()    第25节:ES6+ copyWithin()    第26节:ES6+ fill()    第27节:ES6+ isArray()    第28节:ES6+ 对象的扩展    第29节:ES6+ flat()    第30节:ES6+ 可选链操作符    第31节:ES6+ Object.is()    第32节:ES6+ Object.assign()    第33节:ES6+ Object.keys()    第34节:ES6+ Object.values()    第35节:ES6+ Object.entries()    第36节:ES6+ 数据结构扩展    第37节:ES6+ Set    第38节:ES6+ WeakSet    第39节:ES6+ Map    第40节:ES6+ WeakMap    第41节:ES6+ Symbol    第42节:ES6+ for...of    第43节:ES6+ 迭代协议    第44节:ES6+ 实现一个简版的 Promise    第45节:ES6+ Promise 基础    第46节:ES6+ Promise 进阶    第47节:ES6+ Generator 基础    第48节:ES6+ Generator 函数应用    第49节:ES6+ async/await    第50节:ES6+ Class 前置知识    第51节:ES6+ Class    第52节:ES6+ Proxy    第53节:ES6+ Reflect(一)    第54节:ES6+ Reflect(二)    第55节:ES6+ 模块化(一)    第56节:ES6+ 模块化(二)    第57节:ES6实战1-实现Vue3 reactive 源码    第58节:ES6实战2-实现 Vue3 effect 源码    第59节:ES6 实战2-封装请求    第60节:ES6+ 实战3-代码整洁之道    第61节:ES6 Map原理分析    第62节:ES6module语法加载importexport    第63节:ES6的循环与可迭代对象示例详解