标准答案

  1. 状态机:Promise 至少有 pending、fulfilled、rejected 三种状态。状态只能从 pending 变为 fulfilled 或 rejected,一旦确定就不可逆。
  2. executor:new Promise 时 executor 会立即同步执行。resolve 用来把状态变为 fulfilled,reject 用来变为 rejected;executor 内部抛错时也应该进入 rejected。
  3. 回调队列:如果 then 调用时 Promise 仍是 pending,需要把成功回调和失败回调分别存入队列;等状态确定后,再异步执行对应队列。
  4. 链式调用:then 必须返回一个新的 Promise。回调返回普通值,新 Promise fulfilled;回调抛错,新 Promise rejected;回调返回 Promise 或 thenable,新 Promise 要等待它的最终状态。

题目解析

基础实现不必完整通过 Promise/A+,但要讲清状态不可逆、回调队列、then 返回新 Promise。

then 返回新 Promise 是链式调用的核心。如果 then 直接返回 this,后续链路的值传递、错误穿透和状态隔离都会出问题。

Promise 回调不是同步立刻执行,而是通过微任务调度。手写时可以用 queueMicrotask 模拟,理解上要和 setTimeout 这种宏任务区分开。

完整实现还要处理 thenable 吸收、循环引用保护、同一个 Promise 多次 then、错误穿透和值穿透。核心版可以先覆盖状态机和链式调用,再逐步补边界。

代码示例

下面是核心版实现,重点展示状态不可逆、回调队列和 then 返回新 Promise。

JavaScript
class MyPromise {
  constructor(executor) {
    this.state = 'pending'
    this.value = undefined
    this.handlers = []

    const resolve = (value) => this.settle('fulfilled', value)
    const reject = (reason) => this.settle('rejected', reason)

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

  settle(state, value) {
    if (this.state !== 'pending') return
    this.state = state
    this.value = value
    this.handlers.forEach(handler => this.run(handler))
    this.handlers = []
  }

  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      const handler = { onFulfilled, onRejected, resolve, reject }

      if (this.state === 'pending') {
        this.handlers.push(handler)
      } else {
        this.run(handler)
      }
    })
  }

  run(handler) {
    queueMicrotask(() => {
      const callback = this.state === 'fulfilled'
        ? handler.onFulfilled
        : handler.onRejected

      if (typeof callback !== 'function') {
        const next = this.state === 'fulfilled' ? handler.resolve : handler.reject
        next(this.value)
        return
      }

      try {
        handler.resolve(callback(this.value))
      } catch (error) {
        handler.reject(error)
      }
    })
  }
}

常见误区

  • then 直接返回 this,导致链式调用状态混乱。
  • 忘记 pending 状态下要收集多个回调。
  • 没有处理回调抛错和返回 Promise 的情况。

作者信息