Promises/A+ 规范

一个为js promises开放的标准。

promise 表示一个异步操作的实际结果。 与一个promise交互的主要方法是promise的 then 方法, then方法接收两个回调函数,第一个回调会在promise兑现时被调用,用于接收promise的最终值, 第二个回调会在promise失败时被调用,用于接收promise不能被兑现的原因。

本规范详细描述了 then 方法的行为,提供了一个可操作交互的基础,所有符合 promise/A+ 规范的 promise 都可以依靠该基础来实现。因此这个规范被认为是非常稳定的,尽管 promise/A+ 组织可能偶尔地通过一些较小的向后兼容的改变修订规范, 来解决新发现的一些边界情况。但是,只有在经过仔细的考虑、讨论和测试后,我们才会集成大的或者向后不兼容的更改。

在历史角度上,Promises/A+ 澄清了早期 Promises/A proposal中的行为条款, 将其扩展到涵盖事实上的行为,并省略了未明确说明或有问题的部分。

本规范规范没有涉及如何创建 如何创建、兑现或者拒绝promise,而是选择专注于then方法,未来的工作中,我们可能会提及到这些话题。

术语

  1. “promise” 是一个包含 then方法的对象或者函数,该方法符合规范指定的行为。
  2. “thenable” 是一个定义了 then方法的对象或者函数。
  3. “value” 是任意合法的 Javascript 值 (包括 undefined, thenable, promise)。
  4. “exception” 是一个由 throw语句抛出的值。
  5. “reason” 是一个解释 了为何 promise 被拒绝的值。

要求

promise 状态

必须是以下三种状态值的其中一种: pending, fulfilled, 或者rejected。

  1. 状态是 pending 时:
    1. 可以转换为 fulfilled 或者 rejected。
  2. 当 promise 状态是fulfilled时:
    1. 不能转换为其他值。
    2. 必须包含一个不可变的 value/reason。
  3. promise 状态是rejected 时:
    1. 不能转换为其他值。
    2. 必须包含一个不可变的 value/reason。

在这里的“不可变”代表的是值的引用相同。(i.e. 当 ===为真值时)

then 方法

promise 必须提供then方法来访问当前的值或最终的原因。

promise的 then方法接受2个参数:

promise.then(onFulfilled, onRejected)
  1. onFulfilled and onRejected 都是可选参数:
    1. If onFulfilled不是函数则会被忽略。
    2. If onRejected不是函数则会被忽略。
  2. If onFulfilled 是一个函数:
    1. 它必须在promise 状态转换为fulfilled后调用,promise的结果 作为该函数的第一个参数。
    2. 它不能在 promise被fulfilled前被调用。
    3. 它不能被调用超过一次。
  3. If onRejected 是一个函数:
    1. 它必须在promise状态转换为rejected后调用rejected promise的 拒绝原因作为该函数的第一个参数。
    2. 它不能在 promise 被rejected前被调用。
    3. 它不能被调用超过一次。
  4. onFulfilled or onRejected不能被调用直到 执行上下文 栈仅包含平台代码时才能被调用。 [3.1].
  5. onFulfilled and onRejected必须作为函数被调用 (i.e. 没有 this 值). [3.2]
  6. then 对于同一个promise,可能会被多次调用
    1. 如果/当promise 是fulfilled, 所有 onFulfilled回调函数必须按照最初调用 then的顺序执行。
    2. 如果/当promise 是rejected,所有onRejected回调函数必须按照最初调用 then的顺序执行。
  7. then方法必须返回一个 promise[3.3]。

     promise2 = promise1.then(onFulfilled, onRejected);
    
    1. 如果onFulfilled或者onRejected返回了一个值 x, 则执行 [[Resolve]](promise2, x)解决过程。(详见下文)
    2. 如果onFulfilled or onRejected抛出一个异常 e, promise2必须使用 e作为转为失败状态的原因。
    3. 如果onFulfilled不是一个函数,并且promise1已经兑现,promise2也必须转为兑现状态,同时 使用promise1的兑现值作为兑现值
    4. 如果onRejected不是一个函数,并且promise1已经转为失败状态promise2也必须转为失败状态,同时 使用promise1的失败原因作为失败原因。

处理过程

promise处理过程 是一个抽象的处理输入的promise和x的操作,表示为[[Resolve]](promise, x)。如果x是一个 thenable ,则尝试让promise转换为x的状态,假设x的行为至少有点像promise。 否则将使用x作为promisefulfilled/rejected的值进行处理。

这种 thenable 的特性使得 Promise 的实现更具有通用性, 只要它们暴露了一个符合 promise/A+ 规范的 then 方法, 这同时也使得遵循 promise/A+ 规范的实现可以 与那些不太规范的实现能共存。

要执行[[Resolve]](promise, x)操作,需要执行以下步骤:

  1. 如果promisex是同一个引用, 使用TypeError作为理由拒绝promise。
  2. 如果x是一个promise,则采用它的状态:[3.4]:
    1. 如果x是pending, promise必须保持pending直到 x是被fulfilled或者被rejected。
    2. 如果/当x是fulfilled,使用x的值兑现promise。
    3. 如果/当x是rejected,使用x的值拒绝promise
  3. 否则,如果x是对象或者函数
    1. 尝试将x.then储存至新变量then(then保持对x.then的引用)[3.5]
    2. 如果访问 x.then时收到异常e,使用e拒绝promise
    3. 如果then是一个函数,将x 作为this调用then方法,且第一个参数为resolvePromise,第二个参数为rejectPromise,这里:
      1. 如果resolvePromisey作为第一个参数被回调,执行[[Resolve]](promise, y)
      2. 如果/当rejectPromiser作为拒绝原因被回调,则用 r拒绝promise
      3. 如果resolvePromiserejectPromise 都被调用,或者同一被多次调用,那么第一次调用优先,以后的调用都会被忽略。
      4. 如果调用中then抛出了一个异常e,
        1. 如果resolvePromise或者rejectPromise已经被调用,则忽略。
        2. 否则, 用e作为原因,拒绝promise
    4. 如果then不是一个函数,用x兑现promise
  4. 如果x不是一个函数或者对象,用x兑现 promise

即使promise已经在一个循环thenable链里被resolve,由于[[Resolve]](promise, thenable)的递归特性,最终将导致[[Resolve]](promise, thenable) 被再次调用, 遵循上面的算法会导致无限递归。规范中 并没有要求强制处理,但是鼓励实现者检测递归并且用一个具有丰富信息的TypeError 拒绝 reject promise。[3.6]

说明

  1. "平台代码"指的是引擎,环境,以及 promise 实现代码。 实际上,这个要求确保了onFulfilled and onRejected异步执行,当then方法被调用后, 事件循环队列有了新的任务。这种特性具体实现可以使用宏任务机制,例如setTimeout or setImmediate或者微任务机制,例如 MutationObserver or process.nextTick. 由于考虑了promise在平台的实现,所以它本身可能包含一个任务调度队列来调用回调函数。

  2. 在严格模式下,该函数的this将是undefined; 在传统模式下,this将是全局对象。

  3. 如果实现满足规范所有要求,则可允许promise2 === promise1,但每一个实现都必须明确指出promise2 === promise1 会在怎样的条件下出现。

  4. 通常,只有符合本规范的promise that x才是真正的promise。 This clause allows the use of implementation-specific means to adopt the state of known-conformant promises.

  5. 首先存储对 x.then这一引用,然后测试引用是否有效,然后调用该引用,避免了多次访问 x.then属性,这种预防措施对于确保访问器属性的一致性非常重要, 因为访问器属性的值可能在两次访问中改变。

  6. 实现 不应该限制thenable链的深度。只有真正的死循环才应该引发一个TypeError;如果遇到了一个明确的截然不同的无限调用链,那么无限执行下去才是正确的行为。


CC0
在法律允许的范围内, Promises/A+ 组织放弃 Promises/A+ Promise规范的所有版权和相关或相邻的权利。 本规范被公布于: 美国.