实现 Promise/A+ 规范

手写符合 Promises/A+ 规范的 Promise

问题

从零实现一个符合 Promises/A+ 规范的 Promise。

解答

完整实现

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

class MyPromise {
  constructor(executor) {
    this.status = PENDING
    this.value = undefined
    this.reason = undefined
    // 存储回调函数,处理异步情况
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []

    const resolve = (value) => {
      // 只有 pending 状态才能转换
      if (this.status === PENDING) {
        this.status = FULFILLED
        this.value = value
        // 执行所有成功回调
        this.onFulfilledCallbacks.forEach(fn => fn())
      }
    }

    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED
        this.reason = reason
        // 执行所有失败回调
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }

    // 执行器可能抛出异常
    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onFulfilled, onRejected) {
    // 值穿透:参数不是函数时,创建默认函数
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

    // then 返回新的 Promise
    const promise2 = new MyPromise((resolve, reject) => {
      const fulfilledTask = () => {
        // 使用 queueMicrotask 确保异步执行
        queueMicrotask(() => {
          try {
            const x = onFulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }

      const rejectedTask = () => {
        queueMicrotask(() => {
          try {
            const x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }

      if (this.status === FULFILLED) {
        fulfilledTask()
      } else if (this.status === REJECTED) {
        rejectedTask()
      } else {
        // pending 状态,存储回调
        this.onFulfilledCallbacks.push(fulfilledTask)
        this.onRejectedCallbacks.push(rejectedTask)
      }
    })

    return promise2
  }

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

  finally(callback) {
    return this.then(
      value => MyPromise.resolve(callback()).then(() => value),
      reason => MyPromise.resolve(callback()).then(() => { throw reason })
    )
  }

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

  static reject(reason) {
    return new MyPromise((_, reject) => reject(reason))
  }
}

// 处理 then 返回值的各种情况
function resolvePromise(promise2, x, resolve, reject) {
  // 不能返回自身,防止循环引用
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected'))
  }

  // 防止多次调用
  let called = false

  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      const then = x.then
      if (typeof then === 'function') {
        // x 是 thenable 对象
        then.call(
          x,
          y => {
            if (called) return
            called = true
            // 递归解析,y 可能还是 Promise
            resolvePromise(promise2, y, resolve, reject)
          },
          r => {
            if (called) return
            called = true
            reject(r)
          }
        )
      } else {
        // 普通对象
        resolve(x)
      }
    } catch (error) {
      if (called) return
      called = true
      reject(error)
    }
  } else {
    // x 是原始值
    resolve(x)
  }
}

测试用例

// 基本使用
new MyPromise((resolve) => {
  setTimeout(() => resolve('success'), 100)
})
  .then(value => {
    console.log(value) // 'success'
    return 'next'
  })
  .then(value => {
    console.log(value) // 'next'
  })

// 链式调用返回 Promise
MyPromise.resolve(1)
  .then(v => new MyPromise(resolve => resolve(v + 1)))
  .then(v => console.log(v)) // 2

// 值穿透
MyPromise.resolve('hello')
  .then()
  .then(v => console.log(v)) // 'hello'

// 错误处理
MyPromise.reject('error')
  .catch(e => console.log(e)) // 'error'

关键点

  • 三种状态:pending → fulfilled 或 pending → rejected,状态不可逆
  • 异步执行:回调必须异步执行,使用 queueMicrotasksetTimeout
  • 值穿透then 参数不是函数时,值会传递到下一个 then
  • resolvePromise:处理返回值是 Promise、thenable、普通值的情况
  • 防止循环引用then 返回的 Promise 不能是自身