3 周前 笔记

Promise/A+ 规范实现

Promise 是解决异步编程回调嵌套过多导致代码可读性下降的方法之一。

实现要点

一个 promise 实例有三个状态:pending 等待中、fulfulled 已执行、rejected 已拒绝,状态只会从 pending 切换到 fulfulled 或 rejected,且保持不变。

只有调用了 resolve 或 reject 方法,then 函数的两个参数 onFulfilled 、onRejected 才会被执行,所以当调用 then 时需要用数组将 onFulfilled 、onRejected 存起来,待 resolve 执行后再从数组中取出每个回调函数且异步调用:setTimeout(onFulfilled/onRejected)

实现链式调用 then 可以通过在 then 函数内部返回一个新的 promise。

then 的 onFulfilled 方法执行完毕后可以返回任意值提供给下一个 then 方法,可能会返回一个 promise 或具有 then 接口的对象,所以需要递归调用,实现一个 resolvePromise 方法完成这些功能。

详细的 Promise 规范要求:https://promisesaplus.com/

具体实现

/**
 * promise 各阶段状态
 */
const State = {
  /**
   * 等待中
   */
  PENDING: 'pending',
  /**
   * 已执行
   */
  FULFILLED: 'fulfilled',
  /**
   * 已拒绝
   */
  REJECTED: 'rejected'
}

class Promise {
  constructor(executor) {
    if (!isFunction(executor)) throw new TypeError(`Promise resolver ${executor} is not a function`)

    /**
     * 终值
     */
    this.value = null
    /**
     * 拒绝原因
     */
    this.reason = null
    /**
     * 当前状态,初始为等待中
     */
    this.state = State.PENDING
    /**
     * 已完成状态的 then 待执行回调函数数组
     */
    this.onFulfilledCallbacks = []
    /**
     * 已拒绝状态的 then 待执行回调函数数组
     */
    this.onRejectedCallbacks = []

    const resolve = value => {
      if (this.state === State.PENDING) {
        this.state = State.FULFILLED
        this.value = value
        // 状态为完成后,执行回调数组里的函数
        this.onFulfilledCallbacks.forEach(fn => fn())
      }
    }

    const reject = reason => {
      if (this.state === State.PENDING) {
        this.state = State.REJECTED
        this.reason = reason
        // 状态为拒绝后,执行回调数组里的函数
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }

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

  then (onFulfilled, onRejected) {
    onFulfilled = isFunction(onFulfilled) ? onFulfilled : onDefaultFulfilled
    onRejected = isFunction(onRejected) ? onRejected : onDefaultRejected

    // 返回新 promise 实现链式调用 then
    const promise2 = new this.constructor((resolve, reject) => {
      const onAsyncCallback = (state) => {
        return () => {
          // 规范规定 onFulfilled、onRejected 需要异步执行
          setTimeout(() => {
            try {
              const x = state === State.FULFILLED ? onFulfilled(this.value) : onRejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (err) {
              reject(err)
            }
          })
        }
      }
      // 如果状态处于待执行,先将回调函数存入数组
      if (this.state === State.PENDING) {
        this.onFulfilledCallbacks.push(onAsyncCallback(State.FULFILLED))
        this.onRejectedCallbacks.push(onAsyncCallback(State.REJECTED))
      } else {
        onAsyncCallback(this.state)()
      }
    })
    return promise2
  }

  catch (fn) {
    return this.then(null, fn)
  }
}

Promise.resolve = function (value) {
  return new Promise(resolve => resolve(value))
}

Promise.reject = function (reason) {
  return new Promise((resolve, reject) => reject(reason))
}

Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(resolve, reject)
    }
  })
}

Promise.all = function (promises) {
  const result = []
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then((value) => {
        result.push(value)
        if (result.length === promises.length) {
          resolve(result)
        }
      }, reject)
    }
  })
}

function isFunction (target) { return typeof target === 'function' }
function isObject (target) { return target !== null && typeof target === 'object' }

function onDefaultFulfilled (value) { return value }
function onDefaultRejected (reason) { throw reason }

/**
 * 解决 then 方法返回新 promise 并在链式调用时的一些边界情况
 * @param {Promise} promise2 新的 promise
 * @param {any} x onFulfilled 或 onRejected 返回的值,值可以是任意类型
 * @param {Function} resolve promise2 resolve
 * @param {Function} reject promise2 reject
 */
function resolvePromise (promise2, x, resolve, reject) {
  if (promise2 === x) {
    throw new TypeError(`Chaining cycle detected for promise`)
  }

  // 防止 resolve 和 reject 同时调用
  let called = false

  if (isObject(x) || isFunction(x)) {
    try {
      /**
       * x 的 then 方法可能是通过代理获得的:
       * Object.defineProperty(x, 'then', {
       *  get () { // 干一些不为人知的勾当,也可能会抛出错误 }
       * })
       * 
       * 所以取值后将它缓存,防止触发第二次 getter,并且用 try 包裹,如果出错则 reject
       */
      const then = x.then

      if (isFunction(then)) {
        // 如果是遵循规范的 then 接口,允许它调用
        then.call(x, value => {
          if (called) return
          called = true
          resolvePromise(promise2, value, resolve, reject)
        }, (reason) => {
          if (called) return
          called = true
          reject(reason)
        })
      } else {
        resolve(x)
      }
    }
    catch (err) {
      if (called) return
      called = true
      reject(err)
    }
  } else {
    // x 为原始数值
    resolve(x)
  }
}

// 测试:npx promises-aplus-tests promise.js
Promise.defer = Promise.deferred = function () {
  let dfd = {}
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
}

module.exports = Promise