面试官:手写一下 Promise 吧! 我:What???

概述

哈哈哈,XDM,不好意思啦!有【亿】点点标题党啦。手写整个 Promise 不太可能,但手写 Promise 的部分内容确实是我们经常会遇到,比如手写 Promise.all、Promise.race 等等吧。那为什么面试官喜欢考这些呢?因为 Promise 实在是太重要了,ES6之后,想想我们但凡涉及到异步相关的封装是不是首先想到的就是 Promise!考察 Promise, 不但考察了我们本身对于 Promise 的理解程度,也考察了我们的 JS 功底。
好了,闲言少叙,让我们开始吧!

概念

关于 Promise 的具体概念,这里就不过多叙述了,可以参考 MDN 中的叙述

我们来看一下Promise的API是怎么样的:

  • Promise是一个类,可以翻译成 承诺、许诺 、期约;
  • 当我们需要给予调用者一个承诺:待会儿我会给你回调数据时,就可以创建一个 Promise 的对象;
  • 在通过 new 创建 Promise 对象时,我们需要传入一个回调函数,我们称之为 executor
  • 这个回调函数会被立即执行,并且给传入另外两个回调函数 resolve、reject;
  • 当我们调用 resolve 回调函数时,会执行 Promise 对象的 then 方法传入的回调函数;
  • 当我们调用 reject 回调函数时,会执行 Promise 对象的 catch 方法传入的回调函数;
const promise = new Promise((resolve, reject) => {
  // 调用resolve, 那么then传入的回调会被执行
  resolve("哈哈哈")
  // 调用reject, 那么catch传入的回调会被执行
  reject("错误信息")
})

promise.then(res => {
  console.log(res)
}).catch(err => {
  console.log(err)
})

上面Promise使用过程,我们可以将它划分成三个状态

  • 待定(pending): 初始状态,既没有被兑现,也没有被拒绝;
    • 当执行 executor 中的代码时,处于该状态;
  • 已兑现(fulfilled): 意味着操作成功完成;
    • 执行了 resolve 时,处于该状态;
  • 已拒绝(rejected): 意味着操作失败;
    • 执行了 reject 时,处于该状态;

Promise 有且只有这三种状态,且一旦由 pending 状态转变为 fulfilled 转态或者 rejected 转态,状态就固定不变了,不能够逆转!
接下来我们开始来手写 Promise 吧!不要害怕,我会一步步的带你们手写出来,几乎每行代码都会有注释,整个 Promise 的代码加上注释也不过两百行,跟着我,这一次你一定能把 Promise 拿下!

Promise 成员方法

1. 结构设计:定义 MyPromise 构造函数,并实现基本的状态属性

通过上面的 [概念] 环节相信大家已经知道了 Promise 在实例化的时候会传入一个回调函数 executor,并且立即执行。这个回调函数会接收(之所以用“会接收”而没有用“必须要”,是因为也可以传一个参数进去,原因看到后面你就懂啦~)两个参数 — resolve 和 reject,这两个参数也是以回调函数的形式传入,并且先执行哪一个过后,后面的另一个就不会再执行了,因为先执行的已经将 Promise 的状态锁死了。

这里要做一点说明,事实上,executor 的两个接收的两个回调函数参数 — resolve 和 reject,只要你在内部调用了就一定都会执行。之所以表现为一个执行了,另一个未执行,是因为代码在内部做了限制,所以是内部的代码未执行,而不是回调函数未执行。
举个简单的例子:
function foo() {
if(false) { console.log(“111”) }
}
foo()
调用 foo 函数,foo 执行了吗?肯定执行了,但永远都不会输出 “111”

下面我们一起来看一下代码吧!

class MyPromise {
   // 定义 Promise 的三种状态
   static PENDING = 'pending'
   static FULFILLED = 'fulfilled'
   static REJECTED = 'rejected'

   constructor(executor) {
     // 初始化状态为pending
     this.status = MyPromise.PENDING
     // 初始化成功的值为undefined
     this.value = undefined
     // 初始化失败的原因为undefined
     this.reason = undefined

      // 定义resolve方法
     const resolve = (value) => {
       // 只有在pending状态才能更改状态和值
       if(this.status === MyPromise.PENDING) {
         this.status = MyPromise.FULFILLED
         this.value = value
         console.log("resolve被调用---", value)
       }
     }

     // 定义reject方法
     const reject = (reason) => {
       // 只有在pending状态才能更改状态和原因
       if(this.status === MyPromise.PENDING) {
         this.status = MyPromise.REJECTED
         this.reason = reason
         console.log("reject被调用---", reason)
       }
     }

     // 立即执行执行器函数
     try {
       executor(resolve, reject)
     } catch (error) {
       // 如果执行器函数抛出异常,将Promise状态更改为rejected
       reject(error)
     }
   }
 }

我们来测试一下

const promise = new MyPromise((resolve, reject) => {
   // resolve("11")   // resolve被调用--- 11
   // reject("22")		// reject被调用--- 22
   throw Error("errorrrrrrrr") // reject被调用--- errorrrrrrrr
 })

2. then 方法设计(书接上回,不同的地方就用 —222— 标注吧)

then 方法是整个 Promise 设计中最重要部分,当然,也是最难的部分,我们会分三个小节将其打通。将这个方法掌握以后,后面的就会迎刃而解了。

我们知道 then 方法接受两个参数:

  • fulfilled 的回调函数:当状态变成 fulfilled 时会回调的函数;
  • rejected 的回调函数:当状态变成 rejected 时会回调的函数;

所以 then 函数差不多长这个样子

then(onFulfilled, onRejected) {
  this.onFulfilled = onFulfilled
  this.onRejected = onRejected
}

我们还知道 then 方法里面的代码应该是要被当成微任务进行执行的,所以我们还要创造出这个环境。这里我们使用 queueMicrotask 进行实现。
现在我们一起来看一下整体的代码吧!

class MyPromise {
   // 定义 Promise 的三种状态
   static PENDING = 'pending'
   static FULFILLED = 'fulfilled'
   static REJECTED = 'rejected'

   constructor(executor) {
     // 初始化状态为pending
     this.status = MyPromise.PENDING
     // 初始化成功的值为undefined
     this.value = undefined
     // 初始化失败的原因为undefined
     this.reason = undefined

      // 定义resolve方法
     const resolve = (value) => {
       // 只有在pending状态才能更改状态和值
       if(this.status === MyPromise.PENDING) {
         this.status = MyPromise.FULFILLED
         // ---222---
         queueMicrotask(() => {
           this.value = value
           this.onFulfilled(this.value) 
         }) 
       }
     }

     // 定义reject方法
     const reject = (reason) => {
       // 只有在pending状态才能更改状态和原因
       if(this.status === MyPromise.PENDING) {
         this.status = MyPromise.REJECTED
         // ---222---
         queueMicrotask(() => {
           this.reason = reason
           this.onRejected(this.reason) 
         }) 
       }
     }

     // 立即执行执行器函数
     try {
       executor(resolve, reject)
     } catch (error) {
       // 如果执行器函数抛出异常,将Promise状态更改为rejected
       reject(error)
     }
     
   }
   
   // ---222---
   // then方法接受两个参数: 
   //    fulfilled 的回调函数:当状态变成 fulfilled 时会回调的函数;
   //    reject 的回调函数:当状态变成 rejected 时会回调的函数;
   then(onFulfilled, onRejected) {
     this.onFulfilled = onFulfilled
     this.onRejected = onRejected
   }
 }

我们来测试一下:

const promise = new MyPromise((resolve, reject) => {
  console.log("状态pending")
  // reject(2222)
  resolve(1111)
})

// 调用then方法
promise.then(res => {
  console.log("res1:", res)
  return 1111
}, err => {
  console.log("err:", err)
})
// 输出:
// 状态pending
// res1: 1111

3. then 方法优化1(书接上回,不同的地方就用 —333— 标注吧)

不知道大家有没有发现上述代码中的问题:

  1. 首先,上述代码在 promise 实例化过后,如果紧接着不调用 then 方法,是会报错的,也就是说实例化和 then 方法的调用捆绑了
  2. promise 的 then 方法是可以多次调用的,且每个回调函数都会被执行(也就是 then 里面的 onFulfilled onRejected,注意是 或,不是 和),而上次代码调用多次 then 方法后,只会执行最后一个,因为后面的会将前面的覆盖掉,我们必须使用两个队列将其存储起来,待到合适的时机(也就是 Promise 的状态非 pending 时)去执行。

接下来我们就针对这两个问题进行一下优化吧!
闲言少叙,看代码吧!

 class MyPromise {
 // 定义 Promise 的三种状态
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    // 初始化状态为pending
    this.status = MyPromise.PENDING
    // 初始化成功的值为undefined
    this.value = undefined
    // 初始化失败的原因为undefined
    this.reason = undefined

    // ---333---
     // 初始化成功处理函数队列
    this.onFulfilledFns = []
     // 初始化失败处理函数队列
    this.onRejectedFns = []

     // 定义resolve方法
    const resolve = (value) => {
      // 只有在pending状态才能更改状态和值
      if(this.status === MyPromise.PENDING) {
        // ---222---
        // 添加微任务
        queueMicrotask(() => {
          // ---333---
          if(this.status !== MyPromise.PENDING) return
          this.status = MyPromise.FULFILLED
          this.value = value 
          this.onFulfilledFns.forEach(fn => {
            fn(this.value)
          })
          // this.onFulfilled(this.value)
          // console.log("resolve被调用---", value)
        }) 
      }
    }

    // 定义reject方法
    const reject = (reason) => {
      // 只有在pending状态才能更改状态和原因
      if(this.status === MyPromise.PENDING) { 
        // ---222---
        // 添加微任务
        queueMicrotask(() => {
          // ---333---
          if(this.status !== MyPromise.PENDING) return
          this.status = MyPromise.FULFILLED
          this.reason = reason
          this.onRejectedFns.forEach(fn => {
            fn(this.reason)
          })
          // this.onRejected(this.reason)
          // console.log("reject被调用---", reason)
        }) 
      }
    }

    // 立即执行执行器函数
    try {
      executor(resolve, reject)
    } catch (error) {
      // 如果执行器函数抛出异常,将Promise状态更改为rejected
      reject(error)
    }
    
  }
  
  // ---222---
  // then方法接受两个参数: 
  //    fulfilled 的回调函数:当状态变成 fulfilled 时会回调的函数;
  //    reject 的回调函数:当状态变成 rejected 时会回调的函数;
  then(onFulfilled, onRejected) {
    // ---333---
    // 1.如果在then调用的时候, 状态已经确定下来
    if(this.status === MyPromise.FULFILLED && onFulfilled) {
      onFulfilled(this.value)
    }
    if(this.status === MyPromise.REJECTED && onRejected) {
      onRejected(this.reason)
    }

    // 2.如果在then调用的时候, 状态未确定下来,
    //  则将成功回调和失败的回调放到对应的数组中,
    // 等到状态确定时从对应数组中依次取出执行(队列)
    if(this.status === MyPromise.PENDING) {
      this.onFulfilledFns.push(onFulfilled)
      this.onRejectedFns.push(onRejected)
    } 
  }
}

我们来测试一下:

const promise = new MyPromise((resolve, reject) => {
  console.log("状态pending")
  resolve(1111) // resolved/fulfilled
  reject(2222)
})

// 调用then方法多次调用
promise.then(res => {
  console.log("res1:", res)
}, err => {
  console.log("err:", err)
})

promise.then(res => {
  console.log("res2:", res)
}, err => {
  console.log("err2:", err)
})

// 输出:
// 状态pending
// res1: 1111
// res2: 1111

// 此时 promise 形如
// MyPromise {
//   status: 'fulfilled', value: 1111, reason: undefined,    
//   onFulfilledFns: Array(2), onRejectedFns: Array(2)
// }

4. then 方法优化2(书接上回,不同的地方就用 —444— 标注吧)

经过上述的优化以后,我们的 then 函数已经表现的不错了,但是,大家有没有发现,其实上述代码中还是有一个问题 — promise 是支持链式调用的,也就是可以不断的 .then 但是我们写的代码目前还不支持,那要怎么让它支持链式调用呢? 我们可以将 then 方法整个用一个新的 Promise 包裹,然后返回这个新的 Promise 实例,不就实现了嘛! 对吧, 彦祖亦菲们!~
我们一起来看一下代码吧

class MyPromise {
  // 定义 Promise 的三种状态
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    // 初始化状态为pending
    this.status = MyPromise.PENDING
    // 初始化成功的值为undefined
    this.value = undefined
    // 初始化失败的原因为undefined
    this.reason = undefined

    // ---333---
      // 初始化成功处理函数队列
    this.onFulfilledFns = []
      // 初始化失败处理函数队列
    this.onRejectedFns = []

      // 定义resolve方法
    const resolve = (value) => {
      // 只有在pending状态才能更改状态和值
      if(this.status === MyPromise.PENDING) {
        // ---222---
        // 添加微任务
        queueMicrotask(() => {
          // ---333---
          if(this.status !== MyPromise.PENDING) return
          this.status = MyPromise.FULFILLED
          this.value = value
          // ---333---
          this.onFulfilledFns.forEach(fn => {
            fn(this.value)
          })
          // this.onFulfilled(this.value)
          // console.log("resolve被调用---", value)
        }) 
      }
    }

    // 定义reject方法
    const reject = (reason) => {
      // 只有在pending状态才能更改状态和原因
      if(this.status === MyPromise.PENDING) { 
        // ---222---
        // 添加微任务
        queueMicrotask(() => {
          // ---333---
          if(this.status !== MyPromise.PENDING) return
          this.status = MyPromise.FULFILLED
          this.reason = reason
          this.onRejectedFns.forEach(fn => {
            fn(this.reason)
          })
          // this.onRejected(this.reason)
          // console.log("reject被调用---", reason)
        }) 
      }
    }

    // 立即执行执行器函数
    try {
      executor(resolve, reject)
    } catch (error) {
      // 如果执行器函数抛出异常,将Promise状态更改为rejected
      reject(error)
    }
    
  }
  
  // ---222---
  // then方法接受两个参数: 
  //    fulfilled 的回调函数:当状态变成 fulfilled 时会回调的函数;
  //    reject 的回调函数:当状态变成 rejected 时会回调的函数;
  then(onFulfilled, onRejected) {
    // ---444---
    return new MyPromise((resolve, reject) => {
      // ---333---
      // 1.如果在then调用的时候, 状态已经确定下来
      if(this.status === MyPromise.FULFILLED && onFulfilled) {
        // try {
        //   const value = onFulfilled(this.value) 
        //   resolve(value)
        // } catch (error) {
        //   reject(error)
        // } 
        this.#execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
      }
      if(this.status === MyPromise.REJECTED && onRejected) {
        // try {
        //   const value = onRejected(this.reason) 
        //   resolve(value)
        // } catch (error) {
        //   reject(error)
        // } 
        this.#execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
      }

      // 2.如果在then调用的时候, 状态未确定下来,
      //  则将成功回调和失败的回调放到对应的数组中,
      // 等到状态确定时从对应数组中依次取出执行(队列)
      if(this.status === MyPromise.PENDING) {
        this.onFulfilledFns.push(() => {
          // try {
          //   const value = onFulfilled(this.value)
          //   resolve(value)
          // } catch(err) {
          //   reject(err)
          // }
          this.#execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
        })
        this.onRejectedFns.push(() => {
          // try {
          //   const reason = onRejected(this.reason)
          //   resolve(reason)
          // } catch(err) {
          //   reject(err)
          // }
          this.#execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
        })
      }
    })
  }

  // ---444---
  // 定义一个私有方法进行统一执行处理(工具函数)
  #execFunctionWithCatchError(execFn, value, resolve, reject) {
    try {
      const result = execFn(value)
      resolve(result)
    } catch(err) {
      reject(err)
    }
  } 
} 

我们来测试一下

const promise = new MyPromise((resolve, reject) => {
  console.log("状态pending")
  // resolve(1111) // resolved/fulfilled
  reject(2222)
  // throw new Error("executor error message")
})

// 调用then方法多次调用
promise.then(res => {
  console.log("res1:", res)
  return "aaaa"
  // throw new Error("err message")
}, err => {
  console.log("err1:", err)
  return "bbbbb"
  // throw new Error("err message")
}).then(res => {
  console.log("res2:", res)
}, err => {
  console.log("err2:", err)
})

// 输出:
// 状态pending
// err1: 2222
// res2: bbbbb

5. catch 方法设计(书接上回,不同的地方就用 —555— 表示吧)

能坚持到这里的你真棒!!! 哈哈哈,我们已经将最难的部分跨过去了,相信把 then 方法啃下来的你,接下来的部分对你来说一定 SO EASY!

catch 和 下面的 finally 方法都是 then 方法的语法糖,我们就不过多叙述了,直接看代码吧!

 // ---555---
catch(onRejected) {
  this.then(undefined, onRejected)
}

测试代码:

const promise = new MyPromise((resolve, reject) => {
  console.log("状态pending")
  // resolve(1111) // resolved/fulfilled
  reject(2222)
})

// 调用then方法多次调用
promise.then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err)
})

大家自己跑跑代码看看吧,就不过多叙述了,紧接着,我们就来看一下 finally 方法吧!

6. finally 方法设计(书接上回,不同的地方就用 —666— 表示吧)

闲言少叙,直接看代码吧

// ---666---
// finally 的回调函数不接收参数(传了也没用)
finally(onFinally) {
  this.then(() => {
    onFinally()
  }, () => {
    onFinally()
  })
}

测试代码:

const promise = new MyPromise((resolve, reject) => {
  console.log("状态pending")
  resolve(1111) // resolved/fulfilled
  // reject(2222)
})

// 调用then方法多次调用
promise.then(res => {
  console.log("res1:", res)
  return "aaaaa"
}).then(res => {
  console.log("res2:", res)
}).catch(err => {
  console.log("err:", err)
}).finally(() => {
  console.log("finally")
})

至此,Promise 的成员(实例)方法就全都实现完了,接着来实现类方法吧

Promise 类方法

7. 类方法设计之 resolve - reject(书接上回,不同的地方就用 —777— 表示吧)

这两个方法都是 构造器 constructor 方法执行参数 executor 的语法糖

废话不多说,直接看代码吧

 // ---777---
 static resolve(value) {
   return new MyPromise((resolve) => resolve(value))
 }

 static reject(reason) {
   return new MyPromise((resolve, reject) => reject(reason))
 }

测试代码:

MyPromise.resolve("Hello World").then(res => {
  console.log("res:", res)
})

MyPromise.reject("Error Message").catch(err => {
  console.log("err:", err)
})

接下来我们来看一下 all 和 allSettle 方法吧

8.类方法设计之 all - allSettled(书接上回,不同的地方就用 —888— 表示吧)

all方法:

  • 它的作用是将多个 Promise 包裹在一起形成一个新的 Promise;
  • 新的 Promise 状态由包裹的所有 Promise 共同决定:
    • 当所有的 Promise 状态变成 fulfilled 状态时,新的 Promise 状态为 fulfilled,并且会将所有 Promise 的返回值组成一个数组;
    • 当有一个 Promise 状态为 reject 时,新的 Promise 状态为 reject,并且会将第一个 reject 的返回值作为参数;
// ---888---
static all(promises) {
// 问题关键: 什么时候要执行resolve, 什么时候要执行reject 
  return new MyPromise((resolve, reject) => {
    const results = []
    promises.forEach(promise => {
      promise.then(res => {
        results.push(res)
        if(results.length === promises.length) {
          resolve(results)
        }
      }).catch(err => {
        reject(err)
      })
    })
  })
}

static allSettled(promises) { 
  return new MyPromise((resolve, reject) => {
    const results = []
    promises.forEach(promise => {
      promise.then(res => {
        results.push({status: MyPromise.FULFILLED, value: res })
        if (results.length === promises.length) {
          resolve(results)
        }
      }).catch(err => { 
        results.push({status: MyPromise.REJECTED, reason: err })
        if (results.length === promises.length) {
          resolve(results)
        }
      })
    })
    // 不能将上面分支里面的判断合并成这样,看下面的例子可以知道
    // 由于每个promise 的延时不一样,这样设置的话,结果数组就提前返回为空了
    // setTimeout(() => { 
    //   resolve(results)
    // })
    
  })
}

看一下测试代码:

const p1 = new Promise((resolve) => {
  setTimeout(() => { resolve(1111) }, 1000)
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => { reject(2222) }, 2000)
})
const p3 = new Promise((resolve) => {
  setTimeout(() => { resolve(3333) }, 3000)
})

MyPromise.all([p1, p2, p3]).then(res => {
  console.log(res)
}).catch(err => {
  console.log(err)
})

MyPromise.allSettled([p1, p2, p3]).then(res => {
  console.log(res)
})

9.类方法设计之 race - any(书接上回,不同的地方就用 —999— 表示吧)

最后我们来看一下 race 和 any 方法。闲言少叙,我们直接来看代码吧!

  • race 是竞技、竞赛的意思,表示多个 Promise 相互竞争,谁先有结果,那么就使用谁的结果;

any 方法是 ES12 中新增的方法,和 race 方法是类似的:

  • any 方法会等到一个 fulfilled 状态,才会决定新 Promise 的状态;
  • 如果所有的 Promise 都是 reject 的,那么也会等到所有的 Promise 都变成 rejected 状态;
 // ---999---
static race(promises) {
  return new MyPromise((resolve, reject) => {
    promises.forEach(promise => {
      // promise.then(res => {
      //   resolve(res)
      // }).catch(err => {
      //   reject(err)
      // })
      promise.then(resolve, reject)
    })
  })
}

static any(promises) {
  // resolve必须等到有一个成功的结果
  // reject所有的都失败才执行reject
  const reasons = []
  return new MyPromise((resolve, reject) => {
    promises.forEach(promise => {
      promise.then(res => {
        resolve(res)
      }).catch(err => {
        reasons.push(err)
        if(reasons.length === promises.length) {
          reject(new AggregateError(reasons))
        }
      })
    })
  })
}

测试代码:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => { reject(1111) }, 1000)
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => { reject(2222) }, 2000)
})
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => { resolve(3333) }, 3000)
})


MyPromise.race([p1, p2, p3]).then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err)
})

MyPromise.any([p1, p2, p3]).then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err.errors)
})

手写 Promise 的全部代码

class MyPromise {
 // 定义 Promise 的三种状态
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    // 初始化状态为pending
    this.status = MyPromise.PENDING
    // 初始化成功的值为undefined
    this.value = undefined
    // 初始化失败的原因为undefined
    this.reason = undefined

    // ---333---
      // 初始化成功处理函数队列
    this.onFulfilledFns = []
      // 初始化失败处理函数队列
    this.onRejectedFns = []

      // 定义resolve方法
    const resolve = (value) => {
      // 只有在pending状态才能更改状态和值
      if(this.status === MyPromise.PENDING) {
        // ---222---
        // 添加微任务
        queueMicrotask(() => {
          // ---333---
          if(this.status !== MyPromise.PENDING) return
          this.status = MyPromise.FULFILLED
          this.value = value
          // ---333---
          this.onFulfilledFns.forEach(fn => {
            fn(this.value)
          })
          // this.onFulfilled(this.value)
          // console.log("resolve被调用---", value)
        }) 
      }
    }

    // 定义reject方法
    const reject = (reason) => {
      // 只有在pending状态才能更改状态和原因
      if(this.status === MyPromise.PENDING) { 
        // ---222---
        // 添加微任务
        queueMicrotask(() => {
          // ---333---
          if(this.status !== MyPromise.PENDING) return
          this.status = MyPromise.FULFILLED
          this.reason = reason
          this.onRejectedFns.forEach(fn => {
            fn(this.reason)
          })
          // this.onRejected(this.reason)
          // console.log("reject被调用---", reason)
        }) 
      }
    }

    // 立即执行执行器函数
    try {
      executor(resolve, reject)
    } catch (error) {
      // 如果执行器函数抛出异常,将Promise状态更改为rejected
      reject(error)
    }
    
  }
  
  // ---222---
  // then方法接受两个参数: 
  //    fulfilled 的回调函数:当状态变成 fulfilled 时会回调的函数;
  //    reject 的回调函数:当状态变成 rejected 时会回调的函数;
  then(onFulfilled, onRejected) {
    // ---555---
    // 设置一个默认’拒绝‘函数,确保catch调用时不出错
    const defaultOnRejected = err => { throw err }
    onRejected = onRejected || defaultOnRejected
    // ---666---
    // 设置一个默认’成功‘函数,确保then调用时不出错
    const defaultOnFulfilled = value => { return value }
    onFulfilled = onFulfilled || defaultOnFulfilled

    // ---444---
    return new MyPromise((resolve, reject) => {
      // ---333---
      // 1.如果在then调用的时候, 状态已经确定下来
      if(this.status === MyPromise.FULFILLED && onFulfilled) { 
        this.#execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
      }
      if(this.status === MyPromise.REJECTED && onRejected) { 
        this.#execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
      }

      // 2.如果在then调用的时候, 状态未确定下来,
      //  则将成功回调和失败的回调放到对应的数组中,
      // 等到状态确定时从对应数组中依次取出执行(队列)
      if(this.status === MyPromise.PENDING) {
        this.onFulfilledFns.push(() => { 
          this.#execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
        })
        this.onRejectedFns.push(() => { 
          this.#execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
        })
      }
    })
  }

  // ---444---
  // 定义一个私有方法进行统一执行处理(工具函数)
  #execFunctionWithCatchError(execFn, value, resolve, reject) {
    try {
      const result = execFn(value)
      resolve(result)
    } catch(err) {
      reject(err)
    }
  }

  // ---555---
  catch(onRejected) {
    this.then(undefined, onRejected)
  }

  // ---666---
  // finally 的回调函数不接收参数(传了也没用)
  finally(onFinally) {
    this.then(() => {
      onFinally()
    }, () => {
      onFinally()
    })
  }

  // ---777---
  static resolve(value) {
    return new MyPromise((resolve) => resolve(value))
  }

  static reject(reason) {
    return new MyPromise((resolve, reject) => reject(reason))
  }

  // ---888---
  static all(promises) {
  // 问题关键: 什么时候要执行resolve, 什么时候要执行reject 
    return new MyPromise((resolve, reject) => {
      const results = []
      promises.forEach(promise => {
        promise.then(res => {
          results.push(res)
          if(results.length === promises.length) {
            resolve(results)
          }
        }).catch(err => {
          reject(err)
        })
      })
    })
  }

  static allSettled(promises) { 
    return new MyPromise((resolve, reject) => {
      const results = []
      promises.forEach(promise => {
        promise.then(res => {
          results.push({status: MyPromise.FULFILLED, value: res })
          if (results.length === promises.length) {
            resolve(results)
          }
        }).catch(err => { 
          results.push({status: MyPromise.REJECTED, reason: err })
          if (results.length === promises.length) {
            resolve(results)
          }
        })
      })
    })
  }

  // ---999---
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      promises.forEach(promise => {
        // promise.then(res => {
        //   resolve(res)
        // }).catch(err => {
        //   reject(err)
        // })
        promise.then(resolve, reject)
      })
    })
  }

  static any(promises) {
    // resolve必须等到有一个成功的结果
    // reject所有的都失败才执行reject
    const reasons = []
    return new MyPromise((resolve, reject) => {
      promises.forEach(promise => {
        promise.then(res => {
          resolve(res)
        }).catch(err => {
          reasons.push(err)
          if(reasons.length === promises.length) {
            reject(new AggregateError(reasons))
          }
        })
      })
    })
  }

}

总结

至此,我们就将手写 Promise 的内容全部完成了,相信大家也发觉了,我们在实现了成员方法 then 以后,后面的进度就推进的特别快,几乎没有什么废话,就是直接上代码,为什么呢?因为我们前面说过,整个Promise 中最重要的也是最难得就是这个 then 方法,把它理解掌握了,后面的方法确实也就能一气呵成了。本人水平有限,文章内容难免出错,如果觉得有问题,还望批评指正~
好了,这次就到这里吧,亦菲彦祖们,我们下次见~

参考内容:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

https://mp.weixin.qq.com/s?__biz=Mzg5MDAzNzkwNA==&mid=2247486710&idx=1&sn=faa255d54f78e0297806b077a471e92a&chksm=cfe3ff09f894761f3c1a9bbf85e2cbef1a2b3e4cb6cdbdb040f70b4df63163cd972a2464d649&xtrack=1&scene=0&subscene=244&sessionid=1711941489&clicktime=1711941682&enterid=1711941682&ascene=7&fasttmpl_type=0&fasttmpl_fullversion=7139752-zh_CN-zip&fasttmpl_flag=0&realreporttime=1711941682366&devicetype=android-31&version=2800303b&nettype=3gnet&abtest_cookie=AAACAA%3D%3D&lang=zh_CN&session_us=gh_2c45214772d7&countrycode=CN&exportkey=n_ChQIAhIQQjDXJjXikamFCBdvYk15BhLrAQIE97dBBAEAAAAAAJBWDiJXjDAAAAAOpnltbLcz9gKNyK89dVj0f1aZ1LPwitdZv3s5wfALHbzZ7Tp600VrgOF1hbljxkNTheoVTga0Elvdd7%2B3M%2BZ7RvMxAEHmoMtvU6xM1ABTH1Q2gMQYZKQH3IQJ7VRYxauJbxEh4eQv8EAiypqa4mK%2BWlfl%2FWbcxLH5EF44mt%2FuabuJlaZle2HFaycHj2AzCbs3c%2BSIZbKzEhboqK9gr7vJw%2BqDhS8%2F3UI2q2LGRk26YF7dHHmAaDdb%2FLPpGfBny3wSY1ACD87Dg5bubiHJeV44p5dgZE8%3D&pass_ticket=sT6dXMDD07LNMFn8wiFjN3n%2FPCIO03Oyq6UTk%2FywjC58r2pLlaS9l86Ie%2FJPBl3fV98VB6QUq6hodVUFF3XyYA%3D%3D&wx_header=3

https://mp.weixin.qq.com/s?__biz=Mzg5MDAzNzkwNA==&mid=2247486728&idx=1&sn=3045f09cdd62bb4a0eb2649e77089392&chksm=cfe3fef7f89477e1a5ebfa3fc8c685d184d591dfdf9b64ae5115beb29f1d100d8bb7aad86c3b&xtrack=1&scene=0&subscene=244&sessionid=1711941489&clicktime=1711941519&enterid=1711941519&ascene=7&fasttmpl_type=0&fasttmpl_fullversion=7139752-zh_CN-zip&fasttmpl_flag=0&realreporttime=1711941519749&devicetype=android-31&version=2800303b&nettype=3gnet&abtest_cookie=AAACAA%3D%3D&lang=zh_CN&session_us=gh_2c45214772d7&countrycode=CN&exportkey=n_ChQIAhIQuCssJ0FASZ1BBWpckaLyvBLrAQIE97dBBAEAAAAAAAWBI6W3nYcAAAAOpnltbLcz9gKNyK89dVj0ai9yPa1tosiVKovw48invemLN4FdtNsMcF8CrOJkpAuy1%2BWn71mpoG%2FuGLt3H3I%2Fah3AG35lDIpcpqldKFus9TiFm%2B8TDnT1MaPRzBWd1tcrn%2Ba4lVcIiPrRFM9l9vU%2F0B2%2FUm0bN1JgyIUBKzdw%2BkhuNsq1ULt781CwflOjHjeGy8o%2FqPdFF85LeQGcMC1yTiqWeyzsEePBnsCmV0NfnZ%2B1AklgYgO9YzhktS%2BbGp0SevbXbx%2BM%2Fb%2FROCIA14mR97pqNPM%3D&pass_ticket=sT6dXMDD07LNMFn8wiFjN3n%2FPCIO03Oyq6UTk%2FywjC5eigDJ7ANdHx1joUDdT%2BEbut7M%2F1ZZ51SBvOfAGSVn0g%3D%3D&wx_header=3

  • 22
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值