手写Promise

798 阅读6分钟

实现Promise核心功能

基本功能初步实现

测试代码

 const promise = new MyPromise((resolve, reject) => {
       // resolve('success')
       reject('err')
     })
 ​
     promise.then(value => {
       console.log('resolve', value)
     }, reason => {
       console.log('reject', reason)
     })

初步实现:能够通过MyPromise创建promise实例,通过resolve和reject函数实现改变promise状态和存储结果,给promise实例绑定then函数,then函数能够通过promise实例状态执行相应回调onFulfilled/onRejected

 const PENDING = 'pending'
 const FULFULLED = 'fulfilled'
 const REJECTED = 'rejected'
 ​
 class MyPromise {
   constructor(executor) {
     this.status = PENDING
     this.value = undefined
     this.reason = undefined
 ​
     const resolve = (value) => {
       if (this.status === PENDING) {
         this.status = FULFULLED
         this.value = value
       }
     }
 ​
     const reject = (reason) => {
       if (this.status === PENDING) {
         this.status = REJECTED
         this.reason = reason
       }
     }
 ​
     executor(resolve, reject)
   }
 ​
 ​
   then (onFulfilled, onRejected) {
     if (this.status === FULFULLED) {
       onFulfilled(this.value)
     } else if (this.status === REJECTED) {
       onRejected(this.reason)
     }
   }
 }

异步处理

测试代码修改为

  const promise = new MyPromise((resolve, reject) => {
       setTimeout(() => {
         resolve('success') // 异步执行resolve
       }, 2000);
     })
 ​
     promise.then(value => {
       console.log('resolve', value)
     }, reason => {
       console.log('reject', reason)
     })

会发现控制台没有输出,这是因为then会同步执行,而executor内部异步执行。在进行then内部的状态判断时,promise实例状态还是pending,无法打印测试代码中第8行或第10行

通过在then中存储成功或失败的回调函数(onFulfilled或onRejected),等到executor内部执行resolve或reject时再执行回调onFulfilled或onRejected

 const PENDING = 'pending'
 const FULFULLED = 'fulfilled'
 const REJECTED = 'rejected'
 ​
 class MyPromise {
   constructor(executor) {
     this.status = PENDING
     this.value = undefined
     this.reason = undefined
     // 新增
     // 存储成功与失败的回调
     this.onFulfilledCallback = {}
     this.onRejectedCallback = {}
 ​
     const resolve = (value) => {
       if (this.status === PENDING) {
         this.status = FULFULLED
         this.value = value
         // 新增
         this.onFulfilledCallback && this.onFulfilledCallback(value)
       }
     }
 ​
     const reject = (reason) => {
       if (this.status === PENDING) {
         this.status = REJECTED
         this.reason = reason
         // 新增
         this.onRejectedCallback && this.onRejectedCallback(reason)
       }
     }
 ​
     executor(resolve, reject)
   }
 ​
 ​
   then (onFulfilled, onRejected) {
     if (this.status === FULFULLED) {
       onFulfilled(this.value)
     } else if (this.status === REJECTED) {
       onRejected(this.reason)
     }
     // 新增
     else if (this.status === PENDING) {
       this.onFulfilledCallback = onFulfilled
       this.onRejectedCallback = onRejected
     }
   }
 }
 ​

异步处理多次调用then

测试代码

 const promise = new MyPromise((resolve, reject) => {
       setTimeout(() => {
         resolve('success')
       }, 2000);
     })
 ​
     promise.then(value => {
       console.log('resolve', value)
     })
 ​
     promise.then(value => {
       console.log('resolve', value)
     })
 ​
     promise.then(value => {
       console.log('resolve', value)
     })

期望输出:2s后打印

image.png

实际输出

resolve success

因为多次调用then,后面的成功回调会覆盖前面的,需要将存储回调函数的变量设置为数组

 const PENDING = 'pending'
 const FULFULLED = 'fulfilled'
 const REJECTED = 'rejected'
 ​
 class MyPromise {
   constructor(executor) {
     this.status = PENDING
     this.value = undefined
     this.reason = undefined
     // 修改
     this.onFulfilledCallbacks = []
     this.onRejectedCallbacks = []
 ​
     const resolve = (value) => {
       if (this.status === PENDING) {
         this.status = FULFULLED
         this.value = value
         // 修改
         while (this.onFulfilledCallbacks.length) { // 0 ==> false
           this.onFulfilledCallbacks.shift()(value)
         }
       }
     }
 ​
     const reject = (reason) => {
       if (this.status === PENDING) {
         this.status = REJECTED
         this.reason = reason
         // 修改
         while (this.onRejectedCallbacks.length) {
           this.onRejectedCallbacks.shift()(reason)
         }
       }
     }
 ​
     executor(resolve, reject)
   }
 ​
 ​
   then (onFulfilled, onRejected) {
     if (this.status === FULFULLED) {
       onFulfilled(this.value)
     } else if (this.status === REJECTED) {
       onRejected(this.reason)
     }
     // 修改
     else if (this.status === PENDING) {
       this.onFulfilledCallbacks.push(onFulfilled)
       this.onRejectedCallbacks.push(onRejected)
     }
   }
 }
 ​
 ​

实现then的链式调用

先只考虑成功回调的情况

onFulfilled返回非promise

实现每个then函数返回一个promise,且成功回调返回非promise

  then (onFulfilled, onRejected) {
     if (this.status === RESOLVED) {
       onFulfilled(this.value)
     }
     else if (this.status === REJECTED) {
       onRejected(this.reason)
     }
     else if (this.status === PENDING) { 
       this.onResolvedCallbacks.push(onFulfilled)
       this.onRejectedCallbacks.push(onRejected)
     }
   }

根据规范,每个then必返回一个promise,修改一下上面代码

   then (onFulfilled, onRejected) {
     const promise2 = new MyPromise((resolve, reject) => { // 新增
       if (this.status === FULFULLED) {
         onFulfilled(this.value)
       }
      // else if ...
      // rejected 和 pending 暂时省略
     })
     return promise2 // 新增
   }

测试代码

 const p1 = new MyPromise((res, rej) => {
       res('ok')
     })
     
  p1
  .then(value => {
    console.log(1)
    console.log('resolve', value)
    return 1
  })
  .then(value => {
  console.log(2)
  console.log('resolve', value)
  })

输出结果

1
resolve ok

发现第二个then并没有执行,这是因为第一个then返回的promise实际上是 image.png

主要问题出在第一个.then中new Promise的executor并没有执行resolve或reject来修改promise的状态,所以return的promise2是pending状态,此时再去执行第二个.then,判断promise实例状态时会把onFulfilled函数推入onFulfilledCallbacks数组而不是去执行这个成功回调,也就不会打印

继续修改,定义一个x存储onFulfilled的返回值,加入resolve(x)改变promise2的状态

 then (onFulfilled, onRejected) {
     const promise2 = new MyPromise((resolve, reject) => { 
       if (this.status === FULFULLED) {
         const x = onFulfilled(this.value)//新增
         resolve(x) //新增
       }
      // else if ...
      // rejected 和 pending暂时省略
     })
     return promise2 
   }

此时输出结果与原生Promise一致

image.png

onFulfilled返回promise

测试代码修改5~10,16行

 const p1 = new MyPromise((res, rej) => {
       res('ok')
     })
 ​
     // 新增
     function other () {
       return new MyPromise((resolve, reject) => {
         resolve('other')
       })
     }
 ​
     p1
       .then(value => {
         console.log(1)
         console.log('resolve', value)
         return other() // 返回promise
       })
       .then(value => {
         console.log(2)
         console.log('resolve', value)
       })

输出结果与原生不一致

image.png

这是因为x存储了onFulfilled回调返回的promise(other生成的那个),第一个then的返回值promise2属性值如下

image.png

so需要加一层判断,当x为MyPromise类型时,进行二次处理

 const x = onFulfilled(this.value)
 if (x instanceof MyPromise) {
     x.then(value => resolve(value), reason => reject(reason)) // 新增
 } else {
     resolve(x)
 }

执行完第3行,调用的then中返回的x为

image.png

打印结果🥰🥳

image.png

把这部分业务逻辑抽离出来,在class外面定义一个新的函数resolvePromise()

 class MyPromise {
    //...constructor
    
   then (onFulfilled, onRejected) {
     const promise2 = new MyPromise((resolve, reject) => {
       if (this.status === FULFULLED) {
         const x = onFulfilled(this.value)
         resolvePromise(x, resolve, reject) // 调用resolvePromise
       }
      // else if ...
      // rejected 和 pending暂时省略
     })
     return promise2 // 新增
   }
 }
 ​
 ​
 function resolvePromise (x, resolve, reject) {
   if (x instanceof MyPromise) {
     x.then(value => resolve(value), reason => reject(reason))
   } else {
     resolve(x)
   }
 }

识别Promise是否返回自己

如果 then 方法返回的是自己的 Promise 对象,则会发生循环调用,这个时候程序会报错

 // test.js
 ​
 const promise = new Promise((resolve, reject) => {
   resolve(100)
 })
 const p1 = promise.then(value => {
   console.log(value)
   return p1
 })
 100
 Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

如果then的返回值和onFulfilled返回值相同,说明返回的是自己

修改resolvePromise,创建微任务(等待promise2完成初始化)

 then (onFulfilled, onRejected) {
     const promise2 = new MyPromise((resolve, reject) => {
       if (this.status === FULFULLED) {
         queueMicrotask(() => { // 微任务
           const x = onFulfilled(this.value)
           resolvePromise(promise2, x, resolve, reject)
         })
       }
      // else if ...
      // rejected 和 pending暂时省略
     return promise2 
   }
                                    
                                    
 function resolvePromise (promise2, x, resolve, reject) {
   // 新增调用自己的判断
   if (promise2 === x) {
     return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
   }
   if (x instanceof MyPromise) {
     x.then(value => resolve(value), reason => reject(reason))
   } else {
     resolve(x)
   }
 }                                   

测试代码

 const promise = new MyPromise((resolve, reject) => {
     resolve('success')
 })
  
 // 这个时候将promise定义一个p1,然后返回的时候返回p1这个promise
 const p1 = promise.then(value => {
    console.log(1)
    console.log('resolve', value)
    return p1
 })
  
 // 运行的时候会走reject
 p1.then(value => {
   console.log(2)
   console.log('resolve', value)
 }, reason => {
   console.log(3)
   console.log(reason.message)
 })

结果

image.png

错误捕获

执行器错误

 class MyPromise {
   constructor(executor) {
 ​
     // 省略初始化和resolve、reject
 ​
     try {
       executor(resolve, reject)
     } catch (error) {
       reject(error)
     }
     
   }
 ​
   // 省略then
 }

测试代码

     const p1 = new MyPromise((res, rej) => {
       throw new Error('执行器错误')
     })
 ​
     p1.then((value) => {
       console.log(1);
       console.log(value);
     }, (reason) => {
       console.log(2)
       console.log(reason.message);
     })

image.png

then 执行的时错误捕获

 then (onFulfilled, onRejected) {
     const promise2 = new MyPromise((resolve, reject) => { 
       if (this.status === FULFULLED) {
         queueMicrotask(() => {
           // 新增
           try {
             const x = onFulfilled(this.value)
             resolvePromise(promise2, x, resolve, reject)
           } catch (error) {
             reject(error)
           }
         })
       }
      // else if 
     })
     return promise2 
   }
 }

测试代码

 const promise = new MyPromise((resolve, reject) => {
       resolve('success')
       // throw new Error('执行器错误')
     })
 ​
     // 第一个then方法中的错误要在第二个then方法中捕获到
     promise
       .then(value => {
         console.log(1)
         console.log('resolve', value)
         throw new Error('then error')
       }, reason => {
         console.log(2)
         console.log(reason.message)
       })
       .then(value => {
         console.log(3)
         console.log(value);
       }, reason => { // 打印这里
         console.log(4)
         console.log(reason.message)
       })

image.png

rejected和pending状态

     const p1 = new MyPromise((res, rej) => {
       rej('p1状态为rejected')
     })
 ​
     function other () {
       return new MyPromise((resolve, reject) => {
         resolve('other')
       })
     }
 ​
     p1
       .then(value => {
         console.log(1)
         console.log('resolve', value)
       }, reason => { 
         console.log(2)
         console.log('reject', reason);
         return other()
       })
       .then(value => { 
         console.log(3)
         console.log('resolve', value)
       }, reason => {
         console.log(4)
         console.log('reject', reason);
       })

修改代码

   then (onFulfilled, onRejected) {
     const promise2 = new MyPromise((resolve, reject) => {
       if (this.status === FULFULLED) {
         queueMicrotask(() => {
           try {
             const x = onFulfilled(this.value)
             resolvePromise(promise2, x, resolve, reject)
           } catch (error) {
             reject(error)
           }
         })
       }
       else if (this.status === REJECTED) {
         /* 新增 */
         queueMicrotask(() => {
           try {
             const x = onRejected(this.reason)
             resolvePromise(promise2, x, resolve, reject)
           } catch (error) {
             reject(error)
           }
         })
       }
       else if (this.status === PENDING) {
         /* 新增 */
         this.onFulfilledCallbacks.push(() => {
           queueMicrotask(() => {
             try {
               const x = onFulfilled(this.value)
               resolvePromise(promise2, x, resolve, reject)
             } catch (error) {
               reject(error)
             }
           })
         })
         /* 新增 */
         this.onRejectedCallbacks.push(() => {
           queueMicrotask(() => {
             try {
               const x = onRejected(this.reason)
               resolvePromise(promise2, x, resolve, reject)
             } catch (error) {
               reject(error)
             }
           })
         })
       }
     })
     return promise2
   }
 }

then中参数变为可选

判断then传入的参数,如果不是函数,就使用默认函数

   then (onFulfilled, onRejected) {
     onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
     onRejected = typeof onRejected === 'function' ? onRejected : reason => reason;
 ​
     const promise2 = new MyPromise((resolve, reject) => {
     //...

测试代码

 // test.js
 ​
 const promise = new MyPromise((resolve, reject) => {
   resolve('succ') 
   // reject('err')
 })
  
 promise.then().then().then(value => console.log(value), reason => console.log(reason))
 // 打印 succ / err

实现resolve与reject的静态调用

 class MyPromise {
 ​
    static resolve (parameter) {
        if (parameter instanceof MyPromise) {
          return parameter
        }
 ​
        return new MyPromise(resolve => {
          resolve(parameter)
        })
      }
 ​
      static reject (reason) {
        return new MyPromise((resolve, reject) => {
          reject(reason)
        })
      }
 }

测试代码

 MyPromise.resolve().then(() => {
     console.log(0);
     return MyPromise.resolve(4);
 }).then((res) => {
     console.log(res)
 })

实现Promise.all()

    Promise.myAll = (promises) => {
  // 存放每个resolved promise结果
  let result = []
  // 计数器
  let count = 0
  // 当计数器和数组长度相等,就说明promise全部为resolved
  const len = promises.length

  return new Promise((resolve, reject) => {
    if (len === 0) {
      return resolve([])
    }
    promises.forEach((p, i) => {
      // 注意数组项可能不是promise,需要转化
      Promise.resolve(p).then((res) => {
        count++
        // 收集每个promise返回值
        result[i] = res
        if (count === len) {
          resolve(result)
        }
        // 数组项只要有一个失败,就返回这个失败的promise
      }).catch(reject)
    });
  })
}

// 测试一下
const p1 = Promise.resolve(1)
const p2 = new Promise((resolve) => {
  setTimeout(() => resolve(2), 1000)
})
const p3 = new Promise((resolve) => {
  setTimeout(() => resolve(3), 3000)
})

const p4 = Promise.reject('err4')
const p5 = Promise.reject('err5')
// 1. 所有的Promise都成功了
const p11 = Promise.myAll([p1, p2, p3])
  .then(console.log) // [ 1, 2, 3 ]
  .catch(console.log)

// 2. 有一个Promise失败了
const p12 = Promise.myAll([p1, p2, p4])
  .then(console.log)
  .catch(console.log) // err4

// 3. 有两个Promise失败了,可以看到最终输出的是err4,第一个失败的返回值
const p13 = Promise.myAll([p1, p4, p5])
  .then(console.log)
  .catch(console.log) // err4
// 与原生的Promise.all返回是一致的    


const p14 = Promise.myAll([]).then(console.log).catch(console.log)