初探TS实现一个promise

本文介绍了如何手写一个简易版的Promise类,详细解析了构造函数、resolve、reject方法以及then方法的工作流程。通过示例代码展示了Promise如何处理同步和异步操作,特别强调了在异步场景下回调函数的储存和执行。同时,文章提供了测试用例以加深理解。

关于手写实现一个promise,网上有很多相关的教程,这里推荐一篇文章Promise实现原理(附源码) - 简书,这片文章对promise的实现过程讲解的很明白

1. 我们先来看下面这个代码,这个代码没有严格遵守Promise/A+规范,就是一个简易版的promise,这个代码可以更好理解promise原理(直逼原理)

  • promise代码
class MyPromise {
  status = undefined
  value = undefined
  reason = undefined
  successCallBack = []
  failCallBack = []
  constructor(executor) {
    this.status = 'pending'
    try {
      executor(this.resolve, this.reject)
    } catch (err) {
      this.reject(err)
    }
  }
  resolve = (value) => {
    if (this.status !== 'pending') return
    this.status = 'fulfilled'
    this.value = value
    while (this.successCallBack.length) {
      // shift()方法删除数组中第一项,并返回改项
      this.successCallBack.shift()()
    }
  }
  reject = (reason) => {
    if (this.status !== 'pending') return
    this.status = 'rejected'
    this.reason = reason
    while (this.failCallBack.length) {
      this.failCallBack.shift()()
    }
  }
  then(successCallBack, failCallBack) {
    // successCallback || failCallBack 不存在
    // 为什么要储存回掉函数?看下面这段代码
    // let p = new MyPromise((resolve, reject) => {
    //   setTimeout(() => {
    //     resolve(1)
    //   })
    // }).then(res => do something...)
    // 这时候,因为resolve在异步队列中,他会在本轮时间循环结束后执行,所以这时候的status为pending,
    // 如果传入的为同步的方法,可以理解为上面代码中没有setTimeout,这时status为fulfilled或者rejected
    successCallBack = successCallBack ? successCallBack : value => value
    failCallBack = failCallBack ? failCallBack : reason => reason
    let p2 = new MyPromise((resolve, reject) => {
      if (this.status === 'fulfilled') {
        // 如果为同步代码即没有setTimeout则执行状态为fulfilled或者rejected的代码
        // 当然同步的时候这里直接执行 successCallBack(this.value)就ok
        let x = successCallBack(this.value)
        resolvePromise(p2, x, resolve, reject)
      } else if (this.status === 'rejected') {
        let x = failCallBack(this.reason)
        resolvePromise(p2, x, resolve, reject)
      } else if (this.status === "pending") {
        this.successCallBack.push(() => {
          let x = successCallBack(this.value)
          resolvePromise(p2, x, resolve, reject)
        })
        this.failCallBack.push(() => {
          let x = failCallBack(this.reason)
          resolvePromise(p2, x, resolve, reject)
        })
      }
    })
    return p2
  }

  static resolve(value) {
    if (value instanceof MyPromise) return value
    return new MyPromise((resolve) => resolve(value))
  }

  finally = (callBack) => {
    return this.then(value => {
      return MyPromise.resolve(callBack()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callBack()).then(() => reason)
    })
  }
}
function resolvePromise(promise, x, resolve, reject) {
  if (promise === x) {
    console.log("相同promise")
    return
  }
  if (x instanceof MyPromise) {
    // 传递的是一个new myPromise类型
    x.then(resolve, reject) // 目的是储存当前传递promise的value值,resolve,reject都是当前promise的实例对象的resolve和reject
  } else {
    resolve(x)
  }
}
export { MyPromise }
  • 测试用例
// 同步resolve
// let p = new MyPromise((resolve, reject) => {
//   setTimeout(() => {
//     resolve(1)
//   })
// })
// p.then(res => {
//   return res * 2
// }).then(item => {
//   return item * 2
// }).then(res => {
//   console.log(res)
// })

// 异步resolve
let p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(1)
  })
})
p.then(res => {
  return new MyPromise((resolve, reject) => {
    console.log(res) // 1
    setTimeout(() => {
      resolve(res * 2)
    })
  }) 
}).then(item => {
  return new MyPromise((resolve, reject) => {
    console.log(item) // 2
    setTimeout(() => {
      resolve(item * 2)
    })
  }) 
}).then(res => {
  console.log(res) // 4
})

promise的重点在于promise可以通过.then的方式完成链式调用,所以我们重点关注下then方法:then方法返回了一个promise实例,很多人对于调用resolvePromise这个方法有很多疑惑,这个方法接受4个值,promise:上一个promise实例,x:回掉方法(可能是promise实例或者一个普通的函数),resolve:成功的回掉方法,rejected:失败的回掉方法;

如果promise === x即上一个promise === 当前回掉方法返回的promise实例,则return并暴露一个错误(自己调用自己,用完执行不完),如果x是一个promise实例就调用当前实例的then()方法,此时当前实例的value值(或者rejected)为上一个promise的返回值(注意看测试用例中高亮部分)

为什么要加入successCallBack回掉函数的数组??

当resolve在setTimeout中,即异步方法的时候,代码的执行顺序是先执行了.then()方法,在执行resolve方法。执行.then方法的时候,我们将回掉函数的储存在了回掉数组successCallBan中,等到执行resolve的通过success.shift()()的方法执行各个回掉函数

  • 对应的TS代码
enum Status {
  PENDING = "pending",
  FULFILLED = "fulfilled",
  REJECTED = "rejected"
}

class MyPromise {
  // 定义变量
  status: string | undefined = undefined
  value: any | undefined = undefined
  reason: any | undefined = undefined
  successCallBackArr: Array<() => void> = []
  failCallBackArr: Array<() => void> = []
  // executor接受两个参数: resolve, reject,返回值void
  constructor(executor: (resolve?: (value?: any) => void, reject?: () => void) => void) {
    this.status = Status.PENDING
    try {
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e)
    }
  }

  resolve = (value?: any): void => {
    if (this.status !== Status.PENDING) return
    this.status = Status.FULFILLED
    this.value = value
    while (this.successCallBackArr.length) {
      /**
       * 不能 this.successCallBackArr.shift()(), 报错
       */
      let successCallBackFn = this.successCallBackArr.shift()
      if (successCallBackFn) {
        successCallBackFn()
      }
    }
  }

  reject = (reason?: any): void => {
    if (this.status !== Status.PENDING) return
    this.status = Status.REJECTED
    this.reason = reason
    while (this.failCallBackArr.length) {
      let failCallBack = this.failCallBackArr.shift()
      if (failCallBack) {
        failCallBack()
      }
    }
  }

  // 传递回调函数,有默认值,是个函数
  then(successCallBack: (value: any) => void = value => value, failCallBack: (reason: any) => void = reason => reason): MyPromise {
    let p2 = new MyPromise((resolve: (value: any) => void = this.resolve, reject: (reason: any) => void = this.reject) => {
      if (this.status === Status.FULFILLED) {
        let x = successCallBack(this.value)
        resolvePromise(p2, x, resolve, reject)
      } else if (this.status === Status.REJECTED) {
        let x = failCallBack(this.reason)
        resolvePromise(p2, x, resolve, reject)
      } else if (this.status === Status.PENDING) {
        this.successCallBackArr.push(() => {
          let x = successCallBack(this.value)
          resolvePromise(p2, x, resolve, reject)
        })
        this.failCallBackArr.push(() => {
          let x = failCallBack(this.reason)
          resolvePromise(p2, x, resolve, reject)
        })
      }
    })
    return p2
  }

  catch(failCallBack: (reason: any) => void) {
    return this.then(undefined, failCallBack)
  }
}

function resolvePromise(p2: MyPromise, x: any, resolve: (value: any) => void, reject: (value: any) => void): void {
  if (p2 === x) {
    return reject(new TypeError('循环返回相同的peomise对象'))
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject)
  } else {
    resolve(x)
  }
}

如有不对或理解不周之处,还望指出,如有不懂之处,可以对比文章开头指出的文章对比来看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值