all方法 手写promise_实现一个Promise

类似的文章已经数不清了,但我随意挑了几个看看发现要么太复杂,要么甚至是错误的,此外也为了个人学习之用,就手写一个最简单的 promise 。先把几个要实现的关键要点列出来:

  1. Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的;
  2. promise .then 可以链式调用,但并不是返回 this,而是返回一个新的 promise 实例;
  3. promise .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透;
  4. 同一个 promise 实例可以多次 then,每一个 then 都是返回一个新的 promise 实例;
  5. promise 只有三种状态,状态一经 PENDING -> FULLFILLED 或者 PENDING -> REJECTED 就不会再变化;
  6. 在 promise 中抛出一个错误就直接 reject;
  7. then 函数接收的回调函数中 return 一个 promise 的话,就根据这个 promise 的状态来决定 then 函数 return 的状态,回调函数抛出错误的话 then 函数返回的 promise 状态就为 reject,其余情况默认返回状态为 resolve 的 promise,回调函数为 return 的值。
  8. all 方法返回一个 promise,必须要接收的数组中的 promise 全部 resolve 才会 resolve,有一个 reject 就会 reject

下面将会基于上述几点实现构造函数,实例 then 方法,静态 all 方法。其余的扩展起来就很简单了。

enum STATE {
  PENDING,
  FULLFILLED,
  REJECTED,
}

class MyPromise<T = unknown> {
  private state = STATE.PENDING
  private data: T | undefined
  private resolveCb: (() => void)[] = []
  private rejectCb: (() => void)[] = []

  static all(myPromises: MyPromise[]) {
    return new MyPromise((resolve, reject) => {
      let count = 0
      const resArr: any[] = []
      myPromises.forEach((cb) => {
        cb.then(
          (val: any) => {
            count++
            resArr.push(val)
            if (count === myPromises.length) {
              resolve(resArr)
            }
          },
          (reason: any) => {
            reject(reason)
          }
        )
      })
    })
  }

  constructor(
    executor: (
      resolve: (val?: T) => void,
      reject: (reason?: any) => void
    ) => void
  ) {
    const doResolve = (val?: T) => {
      // 状态只能改变一次
      if (this.state !== STATE.PENDING) {
        return
      }
      this.state = STATE.FULLFILLED
      this.data = val
      // 一个 promise 可以接多个 then
      setTimeout(() => this.resolveCb.forEach((cb) => cb()))
    }
    const doReject = (reason: any) => {
      if (this.state !== STATE.PENDING) {
        return
      }
      this.state = STATE.REJECTED
      this.data = reason
      setTimeout(() => this.rejectCb.forEach((cb) => cb()))
    }

    // 抛出错误就当作 reject
    try {
      executor(doResolve, doReject)
    } catch (e) {
      doReject(e)
    }
  }
  then(sucsCb: any, failCb: any) {
    // 非函数则值穿透,实际上就是将当前 then 接收到的值(this.data)接着传给下一个 then
    if (!isFunction(sucsCb)) {
      sucsCb = () => this.data
    }
    if (!isFunction(failCb)) {
      failCb = () => this.data
    }

    // then 返回的是新的 MyPromise 实例
    return new MyPromise((resolve, reject) => {
      // 能触发 handle 函数就说明之前构造出来的 MyPromise 实例的状态已经不是 PENDING
      //  handle 函数中利用闭包找到 resolve/reject 从而改变 then 返回的 MyPromise 实例的状态
      const handle = (cb: (val: any) => any) => {
        let res
        try {
          // 由外部传来的回调函数来决定该实例的状态
          res = cb(this.data)
          if (res instanceof MyPromise) {
            res.then(resolve, reject)
          } else {
            resolve(res)
          }
        } catch (e) {
          // 回调函数抛出错误的话就将 reject
          reject(res)
        }
      }

      if (this.state === STATE.PENDING) {
        this.resolveCb.push(() => handle(sucsCb))
        this.rejectCb.push(() => handle(failCb))
      } 
      else if (this.state === STATE.FULLFILLED) {
        // 调用 doResolve 的那一刻就发生了状态变化,这里使用 setTimeout 确保异步
        setTimeout(() => handle(sucsCb))
      } 
      else if (this.state === STATE.REJECTED) {
        setTimeout(() => handle(failCb))
      } 
      else {
        const dummy: never = this.state
      }
    })
  }
}

function isFunction(res: any): res is Function {
  return typeof res === 'function'
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值