理解Promise原理

参考资料

[1] 我以为我很懂Promise,直到我开始实现Promise/A+规范

[2] 关于promise为什么要使用微任务

前言

Promise 规范有很多,如 Promise/A,Promise/B,Promise/D 以及 Promise/A 的升级版 Promise/A+,最终 ES6 中采用了 [Promise/A+ 规范](Promise/A+ 规范)。

本文是对参考资料1中给出的源代码的阅读笔记,为了尊重原作者,建议还是点击上面链接查看原作者的文章。

另外,我还根据自己的理解,修改了一下代码,因为我觉得执行onRulfilled和onRjected的代码可以抽离出来单独作为单独的函数(因为多处存在复用)。我的修改版本也能通过Promises/A+ Compliance Test Suite。

Promise.constructor()

阅读Promise()源码,我认为从构造函数开始比较合适。

constructor(executor) {
  this.state = PROMISE_STATES.PENDING; // 初始化状态,为PENDING
  this.fulfillQueue = []; // 当前Promise实例状态为fulfilled时执行的回调
  this.rejectQueue = []; // 当前Promise实例状态为rejected时执行的回调

  // resolve.bind(this)返回一个resolve函数的副本,执行该resolve时里面
  // 的this指向的是当前Promise实例。reject.bind(this)同理。
  executor(resolve.bind(this), reject.bind(this))
}

Promise.then()

由于执行executor(resolve.bind(this), reject.bind(this))时可能会确定Promise的状态,确定状态时会执行通过then()注册的回调函数,因此还是先分析then()比较合适。

  then(onFulfilled, onRejected) {
    // 因为then()会返回一个新的Promise对象,因此这里创建promise2
    const promise2 = new MyPromise(() => { })
    /**
     * 该分支的意思是:如果promise当前是fulfilled状态,
     * 则直接执行传入的onFulfilled函数。
     * 
     * 如果不使用nextTick(),executeOnFulfilled会直接执行resolvePromiseWithValue()
     * 或transition(),这两个方法里会去执行通过then()注册的回调。而此时是还没
     * 有在当前Promise实例上调用then()的,因为后面的then()代码还没执行。使用nextTick()
     * 后,会先继续执行js执行栈剩余的代码,里面就包括了通过then()注册回调的代码。
     */
    if (this.state === PROMISE_STATES.FULFILLED) {
      nextTick(() => { executeOnFulfilled(onFulfilled, this.value, promise2) })
    }
    /**
     * 该分支的意思是:如果promise当前是rejected状态,
     * 则直接执行传入的onRejected函数。
     */
    else if (this.state === PROMISE_STATES.REJECTED) {
      nextTick(() => { executeOnRejected(onRejected, this.reason, promise2) })
    }
    /**
     * 该分支的意思是:如果promise当前是pending状态,
     * 则将onFulfilled函数和onRejected函数分别放到对应的队列中去。
     */
    else if (this.state === PROMISE_STATES.PENDING) {
      this.fulfillQueue.push({ handler: onFulfilled, chainedPromise: promise2 })
      this.rejectQueue.push({ handler: onRejected, chainedPromise: promise2 })
    }
    return promise2;
  }

resolve() & reject()

/**
 * 因为resolve得到的是一个js逻辑值,而不同的值会有不同的处理方案,因此
 * 调用resolvePromiseWithValue()
 */
function resolve(value) {
  resolvePromiseWithValue(this, value)
}

/**
 * 因为reject得到的是一个异常,直接进入rejected,然后报错即可
 */
function reject(reason) {
  transition(this, PROMISE_STATES.REJECTED, reason)
}

resolvePromiseWithValue()

/**
 * 
 * @param {*} promise 
 * @param {*} x 取值可能有4种情况:Promise实例、thenable对象、普通对象(包括函数)、其他
 * @param {*} thenableValues 用于检测thenable cycle
 */
function resolvePromiseWithValue(promise, x, thenableValues = []) {
  // 如果promise和x执行同一个对象,则报错
  if (promise === x) {
    transition(promise, PROMISE_STATES.REJECTED, new TypeError('promise and x cannot refer to the same object.'))
  }
  /**
   * 这个分支的意思是:如果promise决议时收到的值是一个Promise实例,
   * 那么x的在确定状态后,会将其状态和值传给promise。
   */
  else if (isPromise(x)) {
    // 如果x已经确定了状态,则直接用transition转移promise的状态
    if (x.state !== PROMISE_STATES.PENDING) {
      transition(promise, x.state, x.state === PROMISE_STATES.FULFILLED ? x.value : x.reason)
    }
    // 如果x的状态还是PENDING,则等x确定状态后,再做处理
    else {
      x.then(value => {
        resolvePromiseWithValue(promise, value, thenableValues)
      }, reason => {
        transition(promise, PROMISE_STATES.REJECTED, reason)
      })
    }
  }
  /**
   * 这个分支的意思是:如果promise决议时收到的值x是一个对象或方法,
   * 则执行x的then(),并将值传给promise。
   * 于此同时,会检查是否出现thenable cycle。thenable cycle指的是
   * 如果类Promise对象的then()中传给resolve()的值也是一个类Promise
   * 对象,并且个新的类Promise对象的then()传给resolve()的值也是一个
   * 类Promise对象...,后来其中一个类Promise对象的then()传给resolve()
   * 的类Promise对象在之前出现过,那么就产生了一个“环”。
   */
  else if (isObject(x) || typeof x === 'function') {
    let isInvoked = false; // 保证只执行一次(我没看懂是什么只执行一次)
    try {
      const then = x.then;
      // 如果x.then是一个函数,则认为x是一个类Promise对象(鸭式辩型)
      if (typeof then === 'function') {
        // 这里的操作我没看懂,为啥不用x.then(value=>{}, reason=>{})
        then.call(x, value => {
          // 如果出现了环,则转为rejected
          if (thenableValues.indexOf(value) !== -1) {
            transition(promise, PROMISE_STATES.REJECTED, new TypeError('there is a thenable cycle that will lead to infinite recursion.'))
          }
          if (!isInvoked) {
            thenableValues.push(value)
            resolvePromiseWithValue(promise, value, thenableValues)
            isInvoked = true;
          }
        }, reason => {
          if (!isInvoked) {
            transition(promise, PROMISE_STATES.REJECTED, reason)
            isInvoked = true;
          }
        })
      }
      // 如果x.then不是一个函数,则x不是类Promise对象,promise转为fulfilled,值就是x
      else {
        transition(promise, PROMISE_STATES.FULFILLED, x)
      }
    } catch (error) {
      if (!isInvoked) {
        transition(promise, PROMISE_STATES.REJECTED, error)
      }
    }
  }
  /**
   * 这个分支的意思是,如果上面情况都不符合的话,promise转为fulfilled,
   * 并且x作为fulfilled的value。
   */
  else {
    transition(promise, PROMISE_STATES.FULFILLED, x)
  }
}

transition()

/**
 * 状态转移
 * @param {*} promise 待转移状态的promise
 * @param {*} targetState 目标状态
 * @param {*} value 转移时附带的值,可能是fulfilled的value,也可能是rejected的reason
 * @returns 
 */
function transition(promise, targetState, value) {
  // 如果当前状态不是PENDING,或者目标状态是PENDING,则直接返回
  if (promise.state !== PROMISE_STATES.PENDING || targetState === PROMISE_STATES.PENDING) {
    return
  }
  // 状态转义后,state将不发生改变
  Object.defineProperty(promise, 'state', {
    configurable: false,
    writable: false,
    enumerable: true,
    value: targetState
  })
  /**
   * 这个分支的意思是:如果目标状态是FULFILLED,则确定value值,
   * 并在下一个循环执行fulfillQueue上的所有handler。
   */
  if (targetState === PROMISE_STATES.FULFILLED) {
    Object.defineProperty(promise, 'value', {
      configurable: false,
      writable: false,
      enumerable: true,
      value
    })
    nextTick(() => {
      promise.fulfillQueue.forEach(({ handler, chainedPromise }) => {
        executeOnFulfilled(handler, value, chainedPromise)
      })
      promise.fulfillQueue = [];
    })
  }
  /**
   * 这个分支的意思是:如果目标状态是REJECTED,则确定value值,
   * 并在下一个循环执行rejectQueue上的所有handler。
   */
  else if (targetState === PROMISE_STATES.REJECTED) {
    Object.defineProperty(promise, 'reason', {
      configurable: false,
      writable: false,
      enumerable: true,
      value
    })
    nextTick(() => {
      promise.rejectQueue.forEach(({ handler, chainedPromise }) => {
        executeOnRejected(handler, value, chainedPromise)
      })
      promise.rejectQueue = [];
    })
  }
}

executeOnFulfilled() & executeOnRejected()

// 执行onFulfilled回调
function executeOnFulfilled(onFulfilled, value, nextPromise) {
  try {
    /**
     * 如果传入的onFulfilled是函数,则执行得到返回结果,然后调用
     * resolvePromiseWithValue(),该方法会根据不同的值执行不同的处理方案。
     */
    if (typeof onFulfilled === 'function') {
      const adoptedValue = onFulfilled(value)
      resolvePromiseWithValue(nextPromise, adoptedValue)
    }
    /**
     * 如果传入的onFulfilled不是函数,则忽略,将当前Promise实例的状态和
     * 值传给下一个Promise实例。
     */
    else {
      transition(nextPromise, PROMISE_STATES.FULFILLED, value)
    }
  }
  /**
   * 如果发生了异常,则转为rejected状态
   */
  catch (error) {
    transition(nextPromise, PROMISE_STATES.REJECTED, error)
  }
}

// 执行onRejected回调
function executeOnRejected(onRejected, reason, nextPromise) {
  // 该函数的注释参考executeOnFulfilled()
  try {
    if (typeof onRejected === 'function') {
      const adoptedValue = onRejected(reason)
      resolvePromiseWithValue(nextPromise, adoptedValue)
    } else {
      transition(nextPromise, PROMISE_STATES.REJECTED, reason)
    }
  } catch (error) {
    transition(nextPromise, PROMISE_STATES.REJECTED, error)
  }
}

nextTick()

// 添加微任务
function nextTick(callback) {
  // 如果是node环境,则通过nextTick()实现添加微任务
  if (typeof process !== 'undefined' && typeof process.nextTick === 'function') {
    process.nextTick(callback)
  } 
  // 如果是浏览器环境,则通过MutationObserver添加微任务
  else {
    const observer = new MutationObserver(callback)
    const textNode = document.createTextNode('1')
    observer.observe(textNode, {
      characterData: true
    })
    textNode.data = '2'
  }
}

isObject() & isPromise()

// 判断是否是Object实例
function isObject(val) {
  return val && typeof val === 'object'
}

// 判断是否是Promise实例
function isPromise(val) {
  return val instanceof MyPromise
}

为什么Promise要产生微任务

从上面的代码中可以看出,如果只有在执行onFulfilled回调或onRejected回调时才会用到nextTick(),这样的原因是,想把回调的执行放到下一个事件循环。如果不使用nextTick的话,在resolve()执行时,就会直接去执行onFulfilled回调,而由于主线程是单线程的,后面的then()代码还没执行到,回调还没注册,因此就执行不到任何回调。而通过nextTick()将回调的执行放到下一个事件循环后,执行栈的代码可以继续执行,then()处的代码就能执行到,从而在promise上注册回调。

let promise = new Promise((resolve, reject) => {
  resolve(100)
})
.then((value) => {
  console.log(value)
})
上一篇:在使用Windows时防止电脑死机的技巧


下一篇:hive基础-数据模型