一次性教会你手写完整Promise

375 阅读7分钟

promise我们都比较熟悉,我们经常用于异步处理,接下来我们一起用简单明了的方法,手写promise。

基本代码

   const p1 = new Promise((resolve, reject)=>{
   })
   
   const p2 = new Promise((resolve, reject)=>{
     resolve('成功')
     reject('失败')
   })
   
   const p3 = new Promise((resolve, reject)=>{
     reject('失败')
   })
   
  console.log(p1) // Promise {<pending>}
  
  console.log(p2) // Promise {<fulfilled>: '成功'}
  
  console.log(p3) // Promise {<rejected>: '失败'}
   

从上面的代码我们可以分析出几个简单的结论:

  1. Promise 是一个类
  2. 在执行这个类的时候,在构造函数里面传递一个执行器进去,并立即执行.
  3. Promise中有三种状态, 分别为pendding(等待)、fulfilled(成功)、rejected(失败)
    • pending --> fulfilled
    • pending --> rejected 状态只能从pending 变更为 fulfilled 或 rejected,且一旦状态确定下来就不可变更。
  4. 执行器接收接收两个参数resolve和reject,这两个参数是两个函数,用来变更状态的
    • resolve: fulfilled
    • reject: rejected

从上面的结论,设计代码如下:

  const PENDING = 'pending'; // 等待
  const FULFILLED = 'fulfilled'; // 成功
  const REJECTED = 'rejected'; // 失败
     
   class MyPromise{
     constructor(excutor){
       excutor(this._resolve,this._reject)
     }
     // promise 状态
     status = PENDING 
     
     // 更改状态的 resolve 方法 
     // 这里用箭头函数,否则要绑定 this,使this的指向永远指向当前的 MyPromise
     _resolve = () => {
       // 判断状态是否为等待,否则阻止执行
       if(status === PENDING) return;
       // 更改为成功状态
       this.status = FULFILLED
     }
     
    // 更改失败的状态 reject 方法
    // 这里用箭头函数,否则要绑定 this,使this的指向永远指向当前的 MyPromise
    _reject = reason => {
      // 判断状态是否为等待,否则阻止执行
      if (this.status !== PEDDING) return
       // 改变为失败状态
       this.status = REJECTED
      }
   }
   
   new MyPromise((resolve, reject) => {
     resolve('') // status--> fulfilled
   })
   

then 方法

我们先看看then方法的调用

   // 立即输出成功
  const p1 = new Promise((resolve, reject) => {
    resolve('成功')
  }).then(res => console.log(res), err => console.log(err))
  // 输出
  p1.then(res => console.log(res))
  // 一秒后输出失败
  const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('失败')
    },1000)
  }).then(res => console.log(res), err => console.log(err))
  
  // 输出5
  const p3 = new Promise((resolve, reject) => {
    resolve(2)
  }).then((res) => res + 3).then(res => console.log(res)) 
  

从 p1 和 p2 分析可以得出: 1. then方法有两个回调方法,如果是成功状态就调用成功回调,如果是失败状态就调用失败回调的方法。2.then 成功回调有一个参数,返回成功的值。失败回调有一个参数,返回失败的原因。代码如下:

    class MyPromise{
     constructor(excutor){
       excutor(this._resolve,this._reject)
     }
     // promise 状态
     status = PENDING 
     // 成功的值
     value = undefined
     // 失败的原因
     reason = undefined
     // 成功的回调
     successCallback = undefined
     // 失败的回调
     failCallback = undefined
     
     // 更改状态的 resolve 方法 
     // 这里用箭头函数,否则要绑定 this,使this的指向永远指向当前的 MyPromise
     _resolve = value => {
       // 判断状态是否为等待,否则阻止执行
       if(status === PENDING) return;
       // 更改为成功状态
       this.status = FULFILLED
       // 保存成功的值
       this.value = value
     }
     
    // 更改失败的状态 reject 方法
    // 这里用箭头函数,否则要绑定 this,使this的指向永远指向当前的 MyPromise
    _reject = reason => {
      // 判断状态是否为等待,否则阻止执行
      if (this.status !== PEDDING) return
       // 改变为失败状态
       this.status = REJECTED
       // 保存失败的原因
       this.reason = reason
      }
   
   // then
   then(successCallback, failCallback){
     if(this.status === FULFILLED){ // 成功执行成功回调
       successCallback(this.value)
     }else if(this.status === REJECTED){ // 失败执行失败回调
       failCallback(this.reason)
     }
    }
  }

分析p2,p3 还可以得出:3.then方法参数是可选的 4.如果resolve和reject中有异步代码时,异步代码执行完再调回调

    then(successCallback, failCallback){
     // 参数可选
    successCallback = successCallback ? successCallback : value => value
    // 失败参数可选
    failCallback = failCallback ? failCallback : reason => { throw reason }
    
     if(this.status === FULFILLED){ // 成功执行成功回调
       successCallback(this.value)
     }else if(this.status === REJECTED){ // 失败执行失败回调
       failCallback(this.reason)
     }else {
      // 保存回调方法 成功或失败时调用
       this.successCallback = successCallback
       this.failCallback = failCallback
     }
    }
    _resolve = value => {
       // 判断状态是否为等待,否则阻止执行
       if(status === PENDING) return;
       // 更改为成功状态
       this.status = FULFILLED
       // 保存成功的值
       this.value = value
       // 有成功回调就调回调
       this.successCallback && this.successCallback()
     }
     
     _reject = reason => {
      // 判断状态是否为等待,否则阻止执行
      if (this.status !== PEDDING) return
       // 改变为失败状态
       this.status = REJECTED
       // 保存失败的原因
       this.reason = reason
       // 有失败回调就调回调
       this.failCallback && this.failCallback()
     }

p1 中的同个promise的then 方法是可以多次调用的,将我们的代码更改如下

// 把成功和失败的回调的初始值更改为数组
-  successCallback = undefined
-  failCallback = undefined
+  successCallback = []
+  failCallback = []
  
  _resolve= value => {
   ...
-  this.successCallback && this.successCallback()
+  while(this.successCallback.length)this.successCallback.shift()(this.value)
  }
  
  _reject = reason => {
      ...
 -  this.failCallback && this.failCallback()
 +  while(this.failCallback.length)this.failCallback.shift()(this.reason)
  }

then 方法修改如下:

then(successCallback, failCallback){
   successCallback = successCallback ? successCallback : value => value
   // 失败参数可选
   failCallback = failCallback ? failCallback : reason => { throw reason }
   
    if(this.status === FULFILLED){ // 成功执行成功回调
      successCallback(this.value)
    }else if(this.status === REJECTED){ // 失败执行失败回调
      failCallback(this.reason)
    }else {
     // 保存回调方法 成功或失败时调用
    this.successCallback.push(successCallback)
    this.failCallback.push(failCallback)
    }
   }

分析p3:5.then 方法是可以链式调用的,then是promise的原型链的方法,所以then 方法要返回一个promise的对象. 6.上一个then方法的返回值是可以传递给下一个then方法的 7. 判断回调函数的值是普通值还是promise对象,如果是普通值直接resolve 如果是promise对象,根据返回结果调用resolve 或 reject,且循环调用时报错,抽成relovePromise 方法 。promise是微任务 此处只是用setTimeout简单模拟。

resolvePromise 方法:

  function resolvePromise(promise2, x, resolve, reject){
   // 如果返回的promise对象是本身报错
   if(promise2 === x){
     return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
   }
   if(x instanceof MyPromise){
     x.then(resolve, reject)
   } else{
    resolve(x)
   }
  }

then 方法更改如下:

  then(successCallback, failCallback){
    successCallback = successCallback ? successCallback : value => value
    // 失败参数可选
    failCallback = failCallback ? failCallback : reason => { throw reason }
    
     const promise2 = new MyPromise((resolve, reject) => {
      if(this.status === FULFILLED){ // 成功执行成功回调
       setTimeout(() => {
          try {
              let x = successCallback(this.value)
              // 判断x 的值是否为promise对象,如果是则根据返回值调用resolve和reject
              // 普通值直接resolve
              resolvePromise(proimse2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
      }else if(this.status === REJECTED){ // 失败执行失败回调        
        setTimeout(() => {
          try {
             let x = failCallback(this.reason)
              // 判断x 的值是否为promise对象,如果是则根据返回值调用resolve和reject
              // 普通值直接resolve
              resolvePromise(proimse2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
      }else {
        // 保存回调方法 成功或失败时调用
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value)
              // 判断x 的值是否为promise对象,如果是则根据返回值调用resolve和reject
              // 普通值直接resolve
              resolvePromise(proimse2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason)
              // 判断x 的值是否为promise对象,如果是则根据返回值调用resolve和reject
              // 普通值直接resolve
              resolvePromise(proimse2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
      }
     }) 
     return promise2
  }

catch 方法

catch 实际是 then 方法的变形

  catch (failCallback) {
    return this.then(undefined, failCallback)
  }

finally 方法

  finally (callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }

静态方法

1. resolve

   static resolve (value) {
     // 如果是promise 直接返回
     if (value instanceof MyPromise) return value;
     return new MyPromise(resolve => resolve(value))
   }

2. reject

  static reject (reason) {
   if (value instanceof MyPromise) return value;
   return new MyPromise((resolve, reject) => reject(reason))
 }

3. all

  • all 方法是解决异步方法调用的顺序问题的,允许按我们调用的顺序返回
  • 接收一个数组作为参数,如果是普通值直接添加到resolve,判断promise对象根据结果处理
  • 返回值是一个promise对象
  • 所有promise成功了才是成功,有一个promise是reject都是返回这个值
 static all (array) {
   let result = [];
   let index = 0;
   return new MyPromise((resolve, reject) => {
     function addData (key, value) {
       result[key] = value
       index++;
       if (index === array.length) {
         resolve(result)
       }
     }

     for (let i = 0; i < array.length; i++) {
       let current = array[i]
       if (current instanceof MyPromise) {
         // pomise
         current.then(value => addData(i, value), reason => reject(reason))
       } else {
         // 普通值
         addData(i, current)
       }
     }
   })
 }

4. allSettle

  • 接收一个数组,如果数组中有非rpomise的项直接resolve
  • 将每项peomise的结果的数组,返回
  static all (array) {
 let result = [];
 let index = 0;
 return new MyPromise((resolve, reject) => {
   function addData (key, status, value) {
     result[key] = {
      status,
      value
     }
     index++;
     if (index === array.length) {
       resolve(result)
     }
   }

   for (let i = 0; i < array.length; i++) {
     let current = array[i]
     if (current instanceof MyPromise) {
       // pomise
       current.then(value => addData(i,FULFILLED,value), reason => addData(i,REJECTED,reason))
     } else {
       // 普通值
       addData(i,FULFILLED,current)
     }
   }
 })
}

5. race

  • 接收一个promise数组,如有非promise项直接resolve
  • 返回最快的那一个promise的值
  static race (array) {
    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < array.length; i++) {
        let current = array[i]
        if (current instanceof MyPromise) {
          current.then(res => {
            resolve(res)
          },
            reason => {
              reject(reason)
            })
        } else {
          resolve(current)
        }
      }
    })
  }

6. any

  • any 与 all 相反
  • 接收一个promise数组,如有非promise项直接成功
  • 如果有一个promise成功,则返回这个成功的结果
  • 如果所有的promise都失败,则报错
static any (array) {
  return new MyPromise((resolve, reject) => {
    let index = 0
    for (let i = 0; i < array.length; i++) {
      let current = array[i]
      if (current instanceof MyPromise) {
        current.then(res => {
          resolve(res)
        },
          reason => {
            index++;
            if (index === array.length) {
              reject(new AggregateError('All promise were rejected'))
            }
          })
      } else {
        resolve(current)
      }
    }
  })
}