JS高级——手写Promise,并搞懂Promise实现原理

一、目录

  1. 定义CYPromise构造函数及基本状态
  2. resolve和reject方法
  3. then方法
  4. catch方法
  5. finally方法
  6. CYPromise.resolve和CYPromise.reject静态方法
  7. CYPromise.all和CYPromise.allSettled静态方法
  8. 实现Promise.race和Promise.any静态方法

二、实现过程

第1步:定义CYPromise构造函数,并实现基本的状态属性

首先,我们需要创建一个名为CYPromise的类,并定义三种状态:pending、fulfilled和rejected。CYPromise类的构造函数将接收一个执行器(executor)函数作为参数。执行器函数会立即执行,并接收两个参数:resolvereject,它们分别用于将Promise状态从pending更改为fulfilled或rejected。

我们还需要在类中实现状态的改变以及状态改变时对应的值(value)或原因(reason)的存储。

下面是CYPromise类的基本结构以及构造函数的实现:

const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

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

    // 定义resolve方法
    const resolve = (value) => {
    // 只有在pending状态才能更改状态和值
      if (this.status === PROMISE_STATUS_PENDING) {
        this.status = PROMISE_STATUS_FULFILLED
        this.value = value
      }
    }

    // 定义reject方法
    const reject = (reason) => {
    // 只有在pending状态才能更改状态和原因
      if (this.status === PROMISE_STATUS_PENDING) {
        this.status = PROMISE_STATUS_REJECTED
        this.reason = reason
      }
    }

    //执行回调函数
    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }
}

在这一步中,我们定义了CYPromise类的基本结构,并实现了构造函数以及状态属性的初始化。我们还定义了resolvereject方法,并在执行器函数中使用它们。如果执行器函数抛出异常,我们会捕获它并将Promise状态更改为rejected。

第2步:实现resolve和reject两个核心方法

接下来,我们需要在CYPromise类中实现两个核心方法:resolvereject。这两个方法用于处理异步操作的结果。我们会将这两个方法从构造函数中提取出来,并作为类的实例方法。同时,我们需要处理异步操作的结果,将成功或失败的处理函数存储在队列中,在resolvereject方法中逐个执行这些处理函数。

下面是CYPromise类的实现:

const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

class CYPromise {
  constructor(executor) {
    // 初始化状态为pending
    this.status = PROMISE_STATUS_PENDING
    // 初始化成功的值为undefined
    this.value = undefined
    // 初始化失败的原因为undefined
    this.reason = undefined
     //保存fulfilled时的回调函数
    this.onFulfilledFns = []
    //保存rejected时的回调函数
    this.onRejectedFns = []

   const resolve = (value) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        // 添加微任务使其在then方法之后调用
        queueMicrotask(() => {
          // 若当前状态不是pending时,结束(当resolve在前面,是为了取消执行reject中的微任务)
          if (this.status !== PROMISE_STATUS_PENDING) return
          this.status = PROMISE_STATUS_FULFILLED
          this.value = value
          // 遍历数组,执行每个回调函数
          this.onFulfilledFns.forEach(fn => {
            fn(this.value)
          })
        });
      }
    }

    const reject = (reason) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        // 添加微任务使其在then方法之后调用
        queueMicrotask(() => {
          // 若当前状态不是pending时,结束(当reject在前面,是为了取消执行resolve中的微任务)
          if (this.status !== PROMISE_STATUS_PENDING) return
          this.status = PROMISE_STATUS_REJECTED
          this.reason = reason
          // 遍历数组,执行每个回调函数
          this.onRejectedFns.forEach(fn => {
            fn(this.reason)
          })
        })
      }
    }

    //执行回调函数
    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }

    then(onFulfilled, onRejected) {
        // 1.如果在then调用的时候, 状态已经确定下来,就直接执行传进来的函数(延迟调用)
        if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
          onFulfilled(this.value)
        }
        if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
          onRejected(this.reason)
        }

        // 2.将成功回调和失败的回调放到数组中(多次调用promise.then时)
        if (this.status === PROMISE_STATUS_PENDING) {
          this.onFulfilledFns.push(onFulfilled)
          this.onRejectedFns.push(onRejected)
        }
      }
}

在这一步中,我们实现了resolvereject方法。当Promise状态从pending变为fulfilled或rejected时,我们将执行相应的处理函数队列中的函数。此外,我们还对构造函数中的异常处理进行了优化。

第3步:实现then方法

接下来,我们需要在CYPromise类中实现then方法,在第2步的基础上进行优化。then方法用于为Promise实例注册成功和失败的处理函数。它返回一个新的Promise实例,以便我们可以链式调用

我们需要实现一个名为execFunctionWithCatchError的工具函数。这个函数用于处理then方法返回的新Promise实例以及它们对成功和失败函数的处理。

首先,我们实现execFunctionWithCatchError工具函数:

// 工具函数
// execFn: 要执行的函数
// value: 传入的值
// resolve: 成功时调用的函数
// reject:失败时调用的函数
function execFunctionWithCatchError(execFn, value, resolve, reject) {
  try {
    const result = execFn(value)
    resolve(result)
  } catch (err) {
    reject(err)
  }
}

接下来,我们在CYPromise类中实现then方法:

const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

// 工具函数
// execFn: 要执行的函数
// value: 传入的值
// resolve: 成功时调用的函数
// reject:失败时调用的函数
function execFunctionWithCatchError(execFn, value, resolve, reject) {
  try {
    const result = execFn(value)
    resolve(result)
  } catch (err) {
    reject(err)
  }
}

class CYPromise {
  constructor(executor) {
    this.status = PROMISE_STATUS_PENDING
    this.value = undefined
    this.reason = undefined
    //保存fulfilled时的回调函数
    this.onFulfilledFns = []
    //保存rejected时的回调函数
    this.onRejectedFns = []

    const resolve = (value) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        // 添加微任务使其在then方法之后调用
        queueMicrotask(() => {
          // 若当前状态不是pending时,结束(当resolve在前面,是为了取消执行reject中的微任务)
          if (this.status !== PROMISE_STATUS_PENDING) return
          this.status = PROMISE_STATUS_FULFILLED
          this.value = value
          // 遍历数组,执行每个回调函数
          this.onFulfilledFns.forEach(fn => {
            fn()//此处没有参数
          })
        });
      }
    }

    const reject = (reason) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        // 添加微任务使其在then方法之后调用
        queueMicrotask(() => {
          // 若当前状态不是pending时,结束(当reject在前面,是为了取消执行resolve中的微任务)
          if (this.status !== PROMISE_STATUS_PENDING) return
          this.status = PROMISE_STATUS_REJECTED
          this.reason = reason
          // 遍历数组,执行每个回调函数
          this.onRejectedFns.forEach(fn => {
            fn()//此处没有参数
          })
        })
      }
    }

    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }

  then(onFulfilled, onRejected) {
    return new CYPromise((resolve, reject) => {
      // 1.如果在then调用的时候, 状态已经确定下来
      if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
        // try {
        //   const value = onFulfilled(this.value)
        //   resolve(value)
        // } catch(err) {
        //   reject(err)
        // }
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
      }
      if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
        // try {
        //   const reason = onRejected(this.reason)
        //   resolve(reason)
        // } catch(err) {
        //   reject(err)
        // }
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
      }

      // 2.将成功回调和失败的回调放到数组中
      if (this.status === PROMISE_STATUS_PENDING) {
        this.onFulfilledFns.push(() => {
          // try {
          //   const value = onFulfilled(this.value)
          //   resolve(value)
          // } catch(err) {
          //   reject(err)
          // }
          execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
        })
        this.onRejectedFns.push(() => {
          // try {
          //   const reason = onRejected(this.reason)
          //   resolve(reason)
          // } catch(err) {
          //   reject(err)
          // }
          execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
        })
      }
    })
  }
}

第4步:实现catch方法

catch方法它等价于调用then方法时仅传入一个失败处理函数。此时要考虑到,上一个promise对象的then函数并没有传入onReject函数,而是把错误抛出去给下一个promise对象(即当前catch调用then函数返回的promise对象)处理,所以在then方法中对未传入onReject函数进行优化,给一个默认的函数,并在函数中抛出异常,才能把错误信息传递给下一个promise对象。

下面是CYPromise类的catch方法实现,并对then方法进行优化:

class CYPromise {
  // ...其他代码

// then方法
  then(onFulfilled, onRejected) {
    // 若then中没有err处理时,抛出错误进入catch中进行处理;若then中有错误处理,则自己进行错误处理
    //主要解决调用then时,没有处理失败函数,而是让catch去处理失败函数时的情况
    const defaultOnRejected = err => {
      throw err
    }
    onRejected = onRejected || defaultOnRejected


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

      // 2.将成功回调和失败的回调放到数组中
      if (this.status === PROMISE_STATUS_PENDING) {
        if (onFulfilled) this.onFulfilledFns.push(() => {
          execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
        })
        if (onRejected) this.onRejectedFns.push(() => {
          execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
        })
      }
    })
  }

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

在这一步中,我们实现了catch方法。它只接受一个参数:onRejected,这个参数是失败的处理函数。我们通过调用then方法并传入undefined作为成功处理函数来实现这个方法。

第5步:实现finally方法

finally方法它等价于调用then方法时,不论成功或失败时,都将调用传入的函数。

另外要注意一点,若在finally之前调用了catch函数,此时要考虑到在catch函数中调用then函数时,onFulfilled参数是undefined的情况,所以在then方法中也要对成功函数为undefined时进行梳理,此时直接把成功值返回即可。

下面是CYPromise类的catch方法实现,并对then方法进行优化:

class CYPromise {
  // ...其他代码

  then(onFulfilled, onRejected) {
    // 若then中没有err处理时,抛出错误进入catch中进行处理;若then中有错误处理,则自己进行错误处理
     //主要解决调用then时,没有处理失败函数,而是让catch去处理失败函数时的情况
    const defaultOnRejected = err => {
      throw err
    }
    onRejected = onRejected || defaultOnRejected

    // 若then中没有onFulfilled函数时,将value返回,进入下一个promise.then中
    // 主要解决catch函数中,调用then方法时,onFulfilled为undefined时的情况
    const defaultOnFulfilled = value => {
      return value
    }
    onFulfilled = onFulfilled || defaultOnFulfilled

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

      // 2.将成功回调和失败的回调放到数组中
      if (this.status === PROMISE_STATUS_PENDING) {
        if (onFulfilled) this.onFulfilledFns.push(() => {
          execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
        })
        if (onRejected) this.onRejectedFns.push(() => {
          execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
        })
      }
    })
  }

  catch (onRejected) {
    return this.then(undefined, onRejected)
  } 
  finally(onFinally) {
    this.then(() => {
      onFinally()
    }, () => {
      onFinally()
    })
  }
}

在这一步中,我们实现了finally方法。它接受一个参数:onFinally,这个参数是一个处理函数。无论Promise实例成功还是失败,这个处理函数都会被调用。我们通过调用then方法并传入两个相同的处理函数来实现这个方法。这两个处理函数分别用于成功和失败的情况,它们都会返回一个新的Promise实例,以确保onFinally是异步执行的。

第6步:实现Promise.resolve和Promise.reject静态方法

接下来,我们需要在CYPromise类中实现两个静态方法:resolvereject。这两个方法可以快速地创建一个已经解决或拒绝的Promise实例。

下面是CYPromise类的resolve和reject静态方法实现:

class CYPromise {
  // ...其他代码

  // 静态方法(类方法)
  static resolve(value) {
    return new CYPromise((resolve) => resolve(value))
  }
  // 静态方法(类方法)
  static reject(reason) {
    return new CYPromise((resolve, reject) => reject(reason))
  }
}

在这一步中,我们实现了resolvereject静态方法。resolve方法接受一个参数:value,用于创建一个已经解决的Promise实例。reject方法接受一个参数:reason,用于创建一个已经拒绝的Promise实例。

第7步:实现Promise.all和Promise.allSettled静态方法

最后,我们需要在CYPromise类中实现两个静态方法:all和allSettled。all方法用于将多个Promise实例包装成一个新的Promise实例,只有当所有的Promise实例都成功时,新的Promise实例才会成功;allSettled方法用于将多个Promise实例包装成一个新的Promise实例,只要所有的Promise实例都完成(成功或失败),新的Promise实例就会成功。

下面是CYPromise类的all和allSettled静态方法实现:

class CYPromise {
  // ...其他代码

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

  static allSettled(promises) {
    return new CYPromise((resolve) => {
      const results = []
      promises.forEach(promise => {
        promise.then(res => {
          results.push({ status: PROMISE_STATUS_FULFILLED, value: res})
          if (results.length === promises.length) {
            resolve(results)
          }
        }, err => {
          results.push({ status: PROMISE_STATUS_REJECTED, value: err})
          if (results.length === promises.length) {
            resolve(results)
          }
        })
      })
    })
  }
}

在这一步中,我们实现了allrace静态方法。all方法接受一个数组参数,该数组包含多个Promise实例。我们遍历这个数组,使用CYPromise.resolve将每个实例包装成一个标准的Promise实例。当所有实例都解决时,我们将结果数组传递给新的Promise实例的resolve方法。race方法的实现类似,我们遍历输入数组,当任何一个实例解决或拒绝时,立即调用新的Promise实例的resolvereject方法。

第8步:实现Promise.race和Promise.any静态方法

接下来,我们需要在CYPromise类中实现两个额外的静态方法:race和any。race方法则是将多个Promise实例包装成一个新的Promise实例,只要其中一个Promise实例成功或失败,新的Promise实例就会立即成功或失败;any方法则是将多个Promise实例包装成一个新的Promise实例,只要其中一个Promise实例成功,新的Promise实例就会立即成功。如果所有实例都失败,新的Promise实例将失败。

下面是CYPromise类的allSettled和any静态方法实现:

class CYPromise {
  // ...其他代码

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

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

在这一步中,我们实现了allSettledany静态方法。allSettled方法接受一个数组参数,该数组包含多个Promise实例。我们遍历这个数组,使用CYPromise.resolve将每个实例包装成一个标准的Promise实例。当所有实例都完成(成功或失败)时,我们将结果数组传递给新的Promise实例的resolve方法。any方法的实现类似,我们遍历输入数组,当任何一个实例解决时,立即调用新的Promise实例的resolve方法。当所有实例都拒绝时,我们将错误数组传递给新的Promise实例的reject方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值