← 返回文章列表
JavaScript·988 次阅读

从零手写一个符合规范的 Promise

从零手写一个符合规范的 Promise

会用 Promise 之后,想真正理解它只能手写一遍。核心结构:构造函数接收执行器,状态只能从 pending → fulfilled/rejected 单向流转。

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

class MyPromise {
  constructor(executor) {
    this.state = PENDING
    this.value = undefined
    this.callbacks = [] // then 在 pending 时先存起来

    const resolve = (value) => {
      if (this.state !== PENDING) return
      this.state = FULFILLED
      this.value = value
      this.callbacks.forEach((cb) => cb.onFulfilled(value))
    }
    const reject = (reason) => {
      if (this.state !== PENDING) return
      this.state = REJECTED
      this.value = reason
      this.callbacks.forEach((cb) => cb.onRejected(reason))
    }
    executor(resolve, reject)
  }

then 的链式调用

then 必须返回一个新 Promise,这是链式调用的关键:

  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      const handle = (fn) => {
        queueMicrotask(() => {
          try {
            const x = fn(this.value)
            x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
          } catch (e) { reject(e) }
        })
      }
      if (this.state === FULFILLED) handle(onFulfilled)
      else if (this.state === REJECTED) handle(onRejected)
      else this.callbacks.push({
        onFulfilled: () => handle(onFulfilled),
        onRejected: () => handle(onRejected),
      })
    })
  }

几个要点:

  • 回调必须包在 queueMicrotask 里——Promise 回调永远是微任务,即使 Promise 已经 resolve
  • then 的回调返回值 x 如果是 thenable,要"解包"后再决定新 Promise 的状态
  • 回调抛异常,新 Promise 要 reject,所以包 try/catch

衍生方法

catch(fn) 就是 then(null, fn);finally 不管成败都执行且透传原值;静态方法:

  • Promise.resolve:值包装,thenable 会同化
  • Promise.all:全部成功才成功,一个失败立即失败
  • Promise.allSettled:等全部落定,返回状态数组
  • Promise.race:第一个落定的为准(可做超时控制)

写完建议跑一遍 promises-aplus-tests 测试套件,能过 872 个用例才算合格。

看到这里了?如果这篇文章帮到过你

请我喝瓶可乐就好 🥤

收款码

评论0 条

还没有评论,来抢沙发~

发表评论

评论需审核后显示

© 2026 许冬博客 · 专注 JS 技术分享

·RSS 订阅·Powered by Nuxt 4