手写简单版 Promise

Promise作为ES6新增的函数,帮助我们解决了回调地狱的难题,让我们的异步代码可以更加清晰简洁,作为一名前端程序员,手写简单版Promise应该是必备的技能。接下来不多说,直接上代码了。

class Commitment {
      static PENDING = '待定'; static FULFILLED = '成功'; static REJECTED = '拒绝';   //  定义Promise的状态
      constructor(func) {
        this.status = Commitment.PENDING;   // 初始状态
        this.result = null   //  调用resolve或者reject时传入的参数结果 这里用null 是因为调用resolve 或者 reject的时候会给结果赋值
        this.resolveCallbacks = []
        this.rejectCallbacks = []    //  这两个数组用来保存resolve和reject函数 当then里面有异步代码时用来调用
        
        try {
          func(this.resolve.bind(this), this.reject.bind(this));   //  需要注意this的指向问题 这里如果不绑定this的话 调用会报错
        } catch (error) {
          this.reject(error)
        }
      }
      resolve(result) {
        setTimeout(() => {
          if (this.status === Commitment.PENDING) {
            this.status = Commitment.FULFILLED;
            this.result = result   // 将传入的结果赋值给实例的result
            this.resolveCallbacks.forEach(callback => {
              callback(result)
            })
          }
        })
      }
      reject(result) {
        setTimeout(() => {
          if (this.status === Commitment.PENDING) {
            this.status = Commitment.REJECTED;
            this.result = result
            this.rejectCallbacks.forEach(callback => {  // 遍历then中的待执行代码 then中有异步操作时需要用
              callback(result)
            })
          }
        })
      }
      then(onFULFILLEN, onREJECTED) {
        return new Commitment((resolve, reject) => {
          onFULFILLEN = typeof onFULFILLEN === 'function' ? onFULFILLEN : () => {};  //  then 里只能传入函数 如果传入的不是函数就赋值为空函数
          onREJECTED = typeof onREJECTED === 'function' ? onREJECTED : () => {};
          if (this.status === Commitment.PENDING) {
            this.resolveCallbacks.push(onFULFILLEN)
            this.rejectCallbacks.push(onREJECTED)
          }
          if (this.status === Commitment.FULFILLED) {
            setTimeout(() => {
              onFULFILLEN(this.result)
            })
          }
          if (this.status === Commitment.REJECTED) {
            setTimeout(() => {
              onREJECTED(this.result)
            })
          }
        })
      }
    }

代码比较多,我们一步步来看,首先用static定义Promise的三种状态,status就是Promise的初始状态,把它设置成PENDING状态。由于new一个Promise是需要传进来一个函数,这里的func就是传进来的函数了,下面两个数组是用来处理Promise中有异步函数时候的,这里先不讲他们两个。
手写简单版 Promise_第1张图片

因为如果在Promise中自己抛出一个错误信息的话,Promise会通过reject函数帮你执行这个错误,而不会让你的控制台报错,所以这里要通过try catch来捕获错误函数,这里如果不用的话控制台就会报错。
另外还要注意一点,func中传入的resolve跟reject函数要用bind来绑定this,不然通过new出来的实例来调用这两个函数的时候this指向会出问题。
手写简单版 Promise_第2张图片
resolve和reject由于要在时间循环的末尾执行的,因此要在外层套一层setTimeout来让它们变成宏任务。
if 条件语句就是判断当前状态是否为PENDING状态,因为只有PENDING状态才能向FULFILLED和FULFILLED状态转变,判断完后就是改变当前的状态了,将传入的参数赋值给实例result,这里的resolveCallbacks是用来存放在Promise中处于异步函数中的函数,遍历这些函数并依次执行,这就是为什么上面定义这个值的时候用一个空数组的原因,数组的规则是先进先出。下面的rejectCallbacks也是一样道理。
手写简单版 Promise_第3张图片
最后就是比较复杂的then函数了,Promise.then()函数这里是可以传两个值的,本人比较菜,之前也不知道,算是学到了,第一个参数是成功时调用的函数,第二个是失败时调用的函数。
为什么要再返回一个我们自己写的实例Commitment呢?因为这里模仿的是Promise的链式调用,也就是Promise.then().then()这种。
由于then需要传入的是函数,Promise在调用then的时候如果传入的不是函数也不会报错,所以这里要通过typeof对参数进行判断,不是函数则给个空函数,这样就不会报错了。
接下来就是对三种状态进行不同的逻辑处理了。
手写简单版 Promise_第4张图片
当我们用promise这样调用时,会先输出 666 而不是 555,这是因为setTimeout本身是一个宏任务,而我们的resolve函数它内部也调用了setTimeout,因此它也是个宏任务,知道事件循环的只是就不难理解为什么会先输出 666 了。
手写简单版 Promise_第5张图片
这里模拟的就是当调用 then 时,遇到异步任务,而resolve或者reject函数在异步中调用,这个时候状态是没法改变的,所以就要处理PENDING时候的异步任务中的函数,把他们加到对应的Callbacks中
在这里插入图片描述
如果是成功状态,那么用一个setTimeout来回调这个传入的函数并把之前保存的result参数传给它。
失败状态也是一样道理的。
在这里插入图片描述
至此一个简单版的Promise函数就实现了,文章写的不太完善,仅供自己复习用。

你可能感兴趣的:(JS,javascript,前端)