Skip to content

手写符合A+规范的Promise

🕒 Published at:

手写Promise,符合A+规范,A+规范只有一个构造函数,一个then方法,其他的不属于A+规范,而是属于ECMAScript规范

Promise A+规范内容

js
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  #state = PENDING
  #result = undefined

  // 这是一个后进前出的队列
  #handler = []
  constructor(executor) {
    // resolve和reject两个函数可以放在MyPromise.prototype上,但是需要处理this指向问题
    const resolve = (data) => {
      this.#changeState(FULFILLED, data)
    }
    const reject = (errorReason) => {
      this.#changeState(REJECTED, errorReason)
    }
    // try...catch只能捕获同步错误,无法捕获异步错误
    try {
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }

  #changeState(state, result) {
    // promise的状态一旦改变就不能再改了
    if (this.#state !== PENDING) return
    this.#state = state
    this.#result = result
  }

  // 判断是不是一个promise
  #isPromiseLike(value) {
    return (
      value !== null &&
      (typeof value === 'object' || typeof value === 'function') &&
      typeof value.then === 'function'
    )
  }

  // 参考vue源码的做法
  // 将函数尽可能地放到微队列中去执行
  #runMicoTask(func) {
    if (typeof process === 'object' && typeof process.nextTick === 'function') {
      // 在Node环境中模拟微队列
      process.nextTick(func)
    } else if (typeof MutationObserver === 'function') {
      // 在浏览器环境中模拟微队列
      const db = new MutationObserver(func)
      const textNode = document.createTextNode('1')
      db.observe(textNode, {
        characterData: true,
      })
      textNode.data = '2'
    } else {
      setTimeout(func, 0)
    }
  }

  #runOne(callback, resolve, reject) {
    this.#runMicoTask(() => {
      if (typeof callback !== 'function') {
        const settled = this.#state === FULFILLED ? resolve : reject
        settled(this.#result)
        return
      }
      try {
        const data = callback(this.#result)
        if (this.#isPromiseLike(data)) {
          data.then(resolve, reject)
        } else {
          resolve(data)
        }
      } catch (err) {
        reject(err)
      }
    })
  }

  // 什么时候执行resolve和reject?
  // 1. 传递的参数不是函数,promise的状态穿透
  // 2. 传递的参数是一个函数
  // 3. 返回结果是Promise
  #run() {
    if (this.#state === PENDING) return
    while (this.#handler.length) {
      const { onFulfilled, onRejected, resolve, reject } = this.#handler.shift()
      if (this.#state === FULFILLED) {
        this.#runOne(onFulfilled, resolve, reject)
      } else {
        this.#runOne(onRejected, resolve, reject)
      }
    }
  }

  // Promise.prototype.then方法返回一个Promise
  // 什么执行onFulfilled,什么时候执行onRejected
  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      this.#handler.push({
        onFulfilled,
        onRejected,
        resolve,
        reject,
      })
      this.#run()
    })
  }
}