找回密码
 立即注册
首页 业界区 业界 手写 PromiseA+ 实现,轻松通过 872 条用例

手写 PromiseA+ 实现,轻松通过 872 条用例

句惫 2025-6-6 19:09:06
手写 Promise/A+ 实现,轻松通过 872 条用例

规范参考:Promise/A+ 规范 - 中文版本
测试工具:https://github.com/promises-aplus/promises-tests
前言

从接触 Promise 到现在,笔者经历了这么个过程:

  • 了解各种 Promise 规范,包括 Promise/A+,但对其具体内容不甚了解。
  • 研究前人的 Promise 实现,自己尝试编写,但测试时经常遇到问题。
  • 苦思冥想,为什么返回值能够决定 Promise 的状态?为什么链式调用要返回一个新的 Promise 而不是 this?我陷入了深深的困惑。
  • 可能是由于执着,我深入研读了 Promise/A+ 规范。尽管起初对一些条款感到困惑,但我依然坚持学习。
  • 逐句翻译、理解规范,逐渐熟悉了它。从最初需要对照规范编写代码,到如今能够手写 Promise,这个过程充满了乐趣,也让我对 Promise 的一些常用方法有了更深的理解。
如果你也在学习 Promise,遇到困难,我建议你对照规范和代码来理解。
实现

在阅读代码之前,你需要明白,代码中的注释并非随意添加,每条注释都对应着 Promise/A+ 规范中的具体条款。
关于规范,它主要包括以下几个部分:

  • 专业术语,你可以简单了解即可。
  • 详细规范,需要逐行理解,包括:

    • 2.1 Promise 的 3 种状态:pending(待定)、fulfilled(已实现)、rejected(已拒绝)。
    • 2.2 Promise 的 then 方法的实现,不同状态下应执行的操作。
    • 2.3 Promise 对某值的决策行为,也称为 Promise 解决过程。

因此,你会在注释中看到类似以下的标记:
  1. // 2.1 (2)(2)
复制代码
它表示,规范对应的 2.1 Promise 状态 下的第 2 个序号下的 第 2 条内容。
即 标题序号 内容序号... 。
下方代码中尽管没有涵盖所有规范条款的注释,但是隐式实现了。
完整代码如下:
  1. function MyPromise(executor) {
  2.   this.state = 'pending' // 2.1 (1)(1) 初始状态,可以转变为其它两种状态,也就是已实现(fulfilled)或已拒绝(rejected)
  3.   this.value = null // 2.1 (2)(2) 必须有一个不可改变的值
  4.   this.reason = null // 2.1 (3)(2) 必须有一个不可改变的原因
  5.   this.onFulfilledCallbacks = []
  6.   this.onRejectedCallbacks = []
  7.   const resolve = (value) => {
  8.     if (this.state !== 'pending') return
  9.     this.state = 'fulfilled'
  10.     this.value = value
  11.     // 2.2 (6)(1) 当 promise 被实现时,所有相应的 onFulfilled 回调必须按它们调用 then 的顺序执行
  12.     this.onFulfilledCallbacks.forEach((callback) => callback(this.value))
  13.   }
  14.   const reject = (reason) => {
  15.     if (this.state !== 'pending') return
  16.     this.state = 'rejected'
  17.     this.reason = reason
  18.     // 2.2 (6)(2) 当 promise 被拒绝时,所有相应的 onRejected 回调必须按它们调用 then 的顺序执行
  19.     this.onRejectedCallbacks.forEach((callback) => callback(this.reason))
  20.   }
  21.   // 如果 then 中对返回的 Promise 执行器做了异常处理,此步可选
  22.   try {
  23.     executor(resolve, reject)
  24.   } catch (e) {
  25.     reject(e)
  26.   }
  27. }
  28. // 2.2 (6) 原型上方法,根据不同状态保证同一个 Promise 上的 then 可被多次调用
  29. MyPromise.prototype.then = function (onFulfilled, onRejected) {
  30.   // 2.2 (1) onFulfilled 和 onRejected 参数可选,若不是函数则忽略(此处略微改造,本质上还是符合规范的,返回值或抛出异常决策链调用)
  31.   const realOnFulfilled =
  32.     typeof onFulfilled === 'function'
  33.       ? onFulfilled // 2.2 (2)
  34.       : (value) => {
  35.           return value
  36.         }
  37.   const realOnRejected =
  38.     typeof onRejected === 'function'
  39.       ? onRejected // 2.2 (3)
  40.       : (reason) => {
  41.           throw reason
  42.         }
  43.   // 2.2 (7) then 必须返回一个 promise
  44.   let promise2 = new MyPromise((resolve, reject) => {
  45.     if (this.state === 'fulfilled') {
  46.       // 2.2 (4) 必须在执行上下文栈仅包含平台代码时才被调用
  47.       queueMicrotask(() => {
  48.         try {
  49.           // 2.2 (5) 必须作为函数调用
  50.           let x = realOnFulfilled(this.value)
  51.           // 2.2 (7)(1) 根据返回值 x 运行 Promise 解决过程 [[Resolve]](promise2, x),此处也内含了规范 2.2 (7)(3) 处理
  52.           resolvePromise(promise2, x, resolve, reject)
  53.         } catch (e) {
  54.           // 2.2 (7)(2) 抛出异常 e,则 promise2 必须以 e 作为原因被拒绝,此处也包含了 2.2 (7)(4) 的处理(非函数时上方默认函数抛出 reason,这里捕捉拒绝,不就是实现了吗)
  55.           reject(e)
  56.         }
  57.       })
  58.     } else if (this.state === 'rejected') {
  59.       queueMicrotask(() => {
  60.         try {
  61.           let x = realOnRejected(this.reason)
  62.           resolvePromise(promise2, x, resolve, reject)
  63.         } catch (e) {
  64.           reject(e)
  65.         }
  66.       })
  67.     } else if (this.state === 'pending') {
  68.       // 如果 Promise 中的异步执行在后,then添加在前,该步骤能保证回调不被忽略,参考观察者 or 发布订阅?
  69.       this.onFulfilledCallbacks.push(() => {
  70.         queueMicrotask(() => {
  71.           try {
  72.             let x = realOnFulfilled(this.value)
  73.             resolvePromise(promise2, x, resolve, reject)
  74.           } catch (e) {
  75.             reject(e)
  76.           }
  77.         })
  78.       })
  79.       this.onRejectedCallbacks.push(() => {
  80.         queueMicrotask(() => {
  81.           try {
  82.             let x = realOnRejected(this.reason)
  83.             resolvePromise(promise2, x, resolve, reject)
  84.           } catch (e) {
  85.             reject(e)
  86.           }
  87.         })
  88.       })
  89.     }
  90.   })
  91.   // 2.2 (7)
  92.   return promise2
  93. }
  94. function resolvePromise(promise, x, resolve, reject) {
  95.   // 2.3 (1) 如果 promise 和 x 指向同一个对象,则以 TypeError 拒绝 promise 作为原因,这里抛出或者 return reject 均可
  96.   if (promise === x)
  97.     throw new TypeError('promise and return value are the same')
  98.   // 根据 Promise 或者 thenable 对象特性,可以直接判断分支如下
  99.   if (x !== null && (typeof x === 'function' || typeof x === 'object')) {
  100.     // 2.3 (3)(3)(3) 和 2.3 (3)(4)(1) 调用优先问题专用变量
  101.     let called = false
  102.     try {
  103.       const then = x.then
  104.       if (typeof then === 'function') {
  105.       // 2.3 (3)(3) 如果 then 是一个函数,则以 x 作为 this 调用它
  106.       then.call(
  107.           x,
  108.           (y) => {
  109.             if (called) return
  110.             called = true
  111.             // 2.3 (3)(3)(1)
  112.             resolvePromise(promise, y, resolve, reject)
  113.           },
  114.           (r) => {
  115.             if (called) return
  116.             called = true
  117.             // 2.3 (3)(3)(2)
  118.             reject(r)
  119.           }
  120.         )
  121.       } else {
  122.         // 2.3 (3)(4) x 为非 thenable 对象(如果 then 不是一个函数,则用 x 来实现 promise)
  123.         resolve(x)
  124.       }
  125.     } catch (e) {
  126.       if (called) return
  127.       called = true
  128.       // 2.3 (3)(2) 如果检索属性 x.then 时抛出异常 e,则以 e 作为原因拒绝 promise
  129.       // 2.3 (3)(3)(4)(2) 如果调用 then 时抛出异常,以 e 作为原因拒绝 promise
  130.       reject(e)
  131.     }
  132.   } else {
  133.     // 2.3 (4) 如果 x 既不是对象也不是函数,则用 x 来实现 promise
  134.     // x 为 null undefined、基本数值等情况
  135.     resolve(x)
  136.   }
  137. }
  138. // 暴露一个接口提供测试的静态方法
  139. MyPromise.deferred = function () {
  140.   const result = {}
  141.   result.promise = new MyPromise((resolve, reject) => {
  142.     result.resolve = resolve
  143.     result.reject = reject
  144.   })
  145.   return result
  146. }
  147. module.exports = MyPromise
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册