天天看点

Promises/A+ 规范(译本)

简介

promise 表示异步操作的最终结果。与 promise 进行交互的主要方式是通过 then 方法,该方法可以注册回调,以接收 promise 的完成值或无法完成 promise 的原因。

该规范详细说明了 then 方法的行为,提供了一个可交互的基础,所有与 Promises/A+ 规范一致的 promise 实现都可以依赖于它。因此,该规范应被视为是非常稳定的。尽管 Promises/A+ 组织有时会通过向后兼容的微小更改来修订此规范,以解决新发现的极端情况,但只有经过仔细考虑,讨论和测试之后,我们才会集成大的或向后不兼容的更改。

从历史上看,Promises/A+ 阐明了较早 Promises/A 提案 的行为条款,将其扩展到涵盖实际行为,并省略了未指定或有问题的部分。

最后,核心的 Promises/A+ 规范不涉及如何创建、完成或拒绝 Promise,而是选择专注于提供可交互的 then 方法。未来的相关规范工作可能会涉及到这些主题。

1. 术语

  • 1.1. "promise"是一个行为符合此规范的,具有 then 方法的对象或函数。
  • 1.2. "thenable"是具有 then 方法的对象或函数。
  • 1.3. "value"是任何合法的 JavaScript 值(包括 undefined,thenable 或 promise)。
  • 1.4. "exception"是使用 throw 语句抛出的值。
  • 1.5. "reason"用于表示 promise 的拒绝原因。

2. 要求

2.1 Promise 状态

一个 promise 必须处于以下三种状态之一:pending, fulfilled 或者 rejected。

2.1.1 当处于 pending 时,一个 promise:

  • 2.1.1.1 可能会转换为 fulfilled 或者 rejected 状态。

2.1.2 当处于 fulfilled 时,一个 promise:

  • 2.1.2.1 不能再被转换为其他任何状态。
  • 2.1.2.2 必须具有一个值,该值绝不能再改变。

2.1.3 当处于 rejected 时,一个 promise:

  • 2.1.3.1 不能再被转换为其他任何状态。
  • 2.1.3.2 必须有一个 reason,并且该 reason 绝不能再改变。

在此,“绝不能再改变”是指自身不变(即 === ),但并不意味着深度不变。

2.2 then 方法

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

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

promise.then(onFulfilled, onRejected);复制代码      

2.2.1 onFulfilled 和 onRejected 都是可选参数:

  • 2.2.1.1 如果 onFulfilled 不是函数,则必须将其忽略。
  • 2.2.1.2 如果 onRejected 不是函数,则必须将其忽略。

2.2.2 如果 onFulfilled 是一个函数:

  • 2.2.2.1 在 promise 完成之后必须调用它,promise 的 value 作为它的第一个参数。
  • 2.2.2.2 不能在 promise 完成之前调用它。
  • 2.2.2.3 不能多次调用它。

2.2.3 如果 onRejected 是一个函数:

  • 2.2.3.1 在 promise 拒绝之后必须调用它,promise 的 reson 作为它的第一个参数。
  • 2.2.3.2 不能在 promise 拒绝之前调用它。
  • 2.2.3.3 不能多次调用它。

2.2.4 在执行上下文堆栈仅包含平台代码之前,不得调用 onFulfilled 或 onRejected。[3.1]

2.2.5 onFulfilled 和 onRejected 必须作为函数调用,即默认调用方式。(比如:没有 this)。[3.2]

2.2.6 then 函数可能会在同一 promise 上多次被调用。

2.2.6.1 如果/当 promise 完成时,所有相应的 onFulfilled 回调必须按照它们对 then 的原始调用的顺序执行(即按照它们通过 then 方法注册的顺序执行)。

2.2.6.2 如果/当 promise 拒绝时,所有相应的 onRejected 回调必须按照它们对 then 的原始调用的顺序执行(即按照它们通过 then 方法注册的顺序执行)。

2.2.7 then 方法必须返回一个 promise[3.3]。

promise2 = promise1.then(onFulfilled, onRejected);复制代码      

2.2.7.1 如果 onFulfilled 或 onRejected 返回值 x,就执行 Promise 决议程序(Promise Resolution Procedure)[[Resolve]](promise2, x)。

2.2.7.2 如果 onFulfilled 或 onRejected 抛出异常 e,则必须以 e 为 reason 拒绝 promise2。

2.2.7.3 如果 onFulfilled 不是函数,并且 promise1 已经完成,则必须用与 promise1 相同的 value 来完成 promise2。

2.2.7.4 如果 onRejected 不是函数,并且 promise1 被拒绝,则必须用与 promise1 相同的 reason 拒绝 promise2。

2.3 Promise 决议程序(Promise Resolution Procedure)

Promise 决议程序是一个抽象操作,将 promise 和 value 作为输入,我们将其表示为[[Resolve]](promise, x)。如果 x 是一个 tenable,并且行为有点像 promise,则它会尝试使 promise 采用 x 的状态。否则,它将会以 x 值作为 value 完成 promise。

这种对 thenables 的处理允许 promise 实现可交互,只要它们公开了与 Promises/A+ 兼容的 then 方法。它还允许 Promises/A+实现用合理的 then 方法“吸收”不符合要求的实现方式。

要运行[[Resolve]](promise, x),请执行以下步骤:

2.3.1 如果 promise 和 x 指向同一个对象,则以 TypeError 作为理由拒绝 promise。

2.3.2 如果 x 是一个 promise,则采用它的状态[3.4]:

  • 2.3.2.1 如果 x 正处于 pending,则 promise 必须一直等待,直到 x 完成或被拒绝。
  • 2.3.2.2 如果/当 x 完成,用相同的 value 完成 promise。
  • 2.3.2.3 如果/当 x 拒绝,用相同的 reason 拒绝 promise。

2.3.3 否则,如果 x 是一个对象或函数:

  • 2.3.3.1 Let then be x.then 。[3.5]
  • 2.3.3.2 如果检索属性 x.then 导致抛出异常 e,则以 e 为理由拒绝 promise。
  • 2.3.3.3 如果 then 是一个函数,用 x 调用它,第一个参数是 resolvePromise,第二个参数是 rejectPromise,其中:
    • 2.3.3.3.4.1. 如果 resolvePromise 或 rejectPromise 被调用,忽略它。
    • 2.3.3.3.4.2. 否则,以 e 为 reason 拒绝 promise。
    • 2.3.3.3.1 如果/当 resolvePromise 被以值 y 调用时,运行[[Resolve]](promise, y)。
    • 2.3.3.3.2 如果/当 rejectPromise 以一个理由 r 被调用时,使用 r 为 reason 拒绝 promise。
    • 2.3.3.3.3 如果同时调用 resolvePromise 和 rejectPromise,或者多次调用同一个参数,则第一个调用具有优先权,后续的调用将被忽略。
    • 2.3.3.3.4 如果调用 then 抛出异常 e:
  • 2.3.3.4 如果 then 不是一个函数,则用 x 完成 promise。

2.3.4 如果 x 不是一个对象或函数,则用 x 完成 promise。

如果一个 promise 被循环的 thenable 链中的 thenable 决议,那么[[Resolve]](promise, thenable)的递归性质最终会导致[[Resolve]](promise, thenable)再次被调用,遵循以上算法将导致无限递归。我们鼓励实现(但不是必需的)检测这种递归,并以提供信息的 TypeError 作为理由拒绝 promise。[3.6]

3. 备注

3.1 平台代码

这里的“平台代码”是指引擎、环境和 Promise 实现代码。在实践中,这一要求确保 onFulfilled 和 onRejected 在调用事件循环后以新的堆栈异步执行。这可以通过“宏任务”机制(如 setTimeout 或 setImmediate)或“微任务”机制(如 MutationObserver 或 process.nextTick)实现. 由于 promise 实现被认为是平台代码,它本身可能包含一个任务调度队列,在其中调用处理程序。

3.2 没有 this

也就是说,在严格模式下,它内部的 this 将会是 undefined;非严格模式下,this 将是全局对象。

3.3 then 方法必须返回一个 promise

只要 Promise 实现满足所有要求,就可以允许 promise2 === promise1。每个实现都应该记录它能否实现,以及在什么条件下,会生成 promise2 === promise1。

3.4 判断 x 是一个 promise

3.5 保存对 x.then 的引用

3.6 thenbles 链