Promises/A+ 规范(译文)
如题,本文为最新的promise/A+规范的中文翻译,格式和原文保持一致,如有错误,欢迎指正。

一个健全的、可互操作的 JavaScript 的开放标准,由开发者制定,供开发者参考。

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

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

结合之前的规范来看,Promises/A+澄清了早期 Promises/A 提案的行为条款,并进行了具体的实现,同时省略了未明确说明或有问题的部分。

最后强调一下,核心 Promises/A+ 规范不涉及如何创建、调用和执行,而是选择专注于提供通用的 then 方法规范指南。上述对于 promises 的操作方法将来在其他规范中可能会提及。

1. 专业术语

1.1 promise: 一个包含then方法的对象或者函数,只要符合这个规范的均可以称为promise
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状态:
  2.1.1.1 promise的可以被转变为fulfilledrejected的任何一个状态
2.1.2 fulfilled状态:
  2.1.2.1 此时promise的状态不能再被改变。
  2.1.2.2 promise有一个结果值,但是也不能再被改变。
2.1.3 rejected状态:
  2.1.3.1 此时promise的状态不能再被改变。
  2.1.3.2 promise有一个被拒绝的原因,同时也不能再被改变。

这里声明一下:不能被改变指的是身份不能被改变,判断逻辑是使用javascript的===进行比较,但是不意味着深度不可被改变(这里可以参考const类型数据的的更改,非引用类型的值不能被修改,但是引用类型的是可以修改数据的属性,只是不能对目标直接执行赋值操作,推测应该是目标的的内存地址不可变)

2.2 then 方法

promise必须提供一个then方法来获取它当前或者最终的值或者被拒绝的理由。promisethen方法接收两个参数,其语法如下:

promise.then(onFulfilled, onRejected)

以下是关于参数onFulfilledonRejected的说明:
2.2.1 onFulfilledonRejected都是可选的参数:
  2.2.1.1 如果 onFulfilled 的类型不是函数,那它将一定被忽略
  2.2.1.2 如果 onRejected 的类型不是函数,那它将一定被忽略
2.2.2 如果 onFulfilled 是一个函数:
  2.2.2.1 它一定会在当前promise状态切换为 fulfilled 时被调用,同时promisevalue作为它的第一个参数。
  2.2.2.2 它一定不能在当前promise的状态切换为 fulfilled 之前被调用。
  2.2.2.3 它只能被调用一次
2.2.3 如果 onRejected 是一个函数
  2.2.3.1 它一定会在当前promise状态切换为 rejected 时被调用,同时promisereason作为它的第一个参数。
  2.2.3.2 它一定不能在当前promise的状态切换为 rejected 之前被调用。
  2.2.3.3 它只能被调用一次
2.2.4 onFulfilledonRejected只有在执行上下文堆栈仅包含平台代码时才可被调用。[3.1]
2.2.5 onFulfilledonRejected必须作为函数进行调用(即没有this值)[3.2]
2.2.6 then 可能会在同一个promise上多次进行调用
  2.2.6.1 如果promise的状态是已完成,所有的相应的 onFulfilled 回调都必须按照调用的顺序执行。
  2.2.6.2 如果promise的状态是拒绝状态,所有的相应的 onRejected 回调都必须按照调用的顺序执行。
2.2.7 then 必须返回一个promise[3.3],如下:

promise2 = promise1.then(onFulfilled, onRejected);

  2.2.7.1 如果 onFulfilledonRejected 返回值一个值 x,则运行Promise解析过程 [[Resolve]](promise2, x)。
  2.2.7.2 如果onFulfilled或者onRejected抛出异常e,则promise2必须以e作为reasonpromise2置为rejected
  2.2.7.3 如果onFulfilled不是函数且promise1成功执行,promise2必须和promise1一样的value将状态置为 fulfilled
  2.2.7.4 如果onRejected不是函数且promise1拒绝执行,promise2必须拒绝执行并返回和promise1相同的reason

2.3 Promise的解决过程

promise解决过程是一个抽象操作,将promise和值作为输入,我们将其表示为[[Resolve]](promise,x)。如果xthenable,这种情况下,可以把x看作是一个promise,此时试图使promise采用x的状态。否则,它将用x作为value来完成promise

这种对thenables的处理允许promise实现更加通用:只要它们对外暴露了符合Promises/a+then方法即可。它还允许Promises/A+实现用合理的方法“兼容”不一致的实现。

运行 [[Resolve]](promise, x), 有以下步骤:

2.3.1 如果 promisex 是同一个对象的引用, 则直接将TypeError作为reason拒绝promise.
2.3.2 如果x是一个promise,则取决于它的状态 [3.4]:
  2.3.2.1 如果x的状态是 pending, promise 必须在 x 被执行和被拒绝之前保持pending状态。
  2.3.2.2 如果x的状态是 fulfilled, 则使用相同的value执行promise.
  2.3.2.3 如果x的状态是rejected,则使用相同的reason拒绝promise.
2.3.3 另外,如果x是一个对象或者函数时:
  2.3.3.1then方法赋值为x.then. [3.5]
  2.3.3.2 如果读取属性x.then导致抛出异常e,则以e作为reason拒绝promise
  2.3.3.3 如果then是一个函数,则将x作为它的this进行调用(可以理解为:x.then().bind(x)),同时它的第一个参数 是resolvePromise, 第二个参数是 rejectPromise,关于这两个参数:
    2.3.3.3.1 如果使用value y调用resolvePromise,则运行[[Resolve]](promise, y).
    2.3.3.3.2 如果使用reason r调用 rejectPromise ,则使用r作为reason拒绝promise
    2.3.3.3.3 如果 resolvePromiserejectPromise 都被调用,或者使用同一参数调用了多次,则优先采用第一次调用并且忽略其他所有后续的调用.
    2.3.3.3.4 如果调用 then 方法出现了一个异常 e
      2.3.3.3.4.1 如果 resolvePromiserejectPromise 已经被调用,则忽略这个异常.
      2.3.3.3.4.2 否则 将上述的e作为 reason 拒绝promise.
  2.3.3.4 如果 then 不是一个函数,则直接使用x作为value来实现promise.
2.3.4 如果x不是一个对象或者函数,则直接使用x作为value来实现promise.

如果一个promise被一个循环的thenable链中的thenable来解析,由于[[Resolve]](promise,thenable)的递归性质最终导致[[Resolve]](prome,thenabe)被再次调用,遵循上述算法将导致无限递归。我们鼓励但不强制要求开发者去检测这种递归,并以提供能够可识别的TypeError作为reason拒绝 promise 。[3.6]

3.注释

3.1

这里的“平台代码”是指引擎、环境和promise实现代码。在实践中,这一要求确保onFulfilledonRejected在调用事件循环之后异步执行,并使用新的堆栈。这可以用“宏任务”机制(如setTimeoutsetImmediate)实现,也可以用“微任务”机制,如MutationObserverprocess.nextTick实现。由于promise实现被视为平台代码,它本身可能包含一个任务调度队列或“蹦床”,在其中调用处理程序。

3.2

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

3.3

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

3.4

一般来说,只有当x来自当前实现时,才能确认它是一个真正的 promise。该条款允许使用特定于实现的手段来采用已知一致承诺的状态。

3.5

这个过程首先存储对x.then的引用,然后测试该引用,然后调用该引用,避免了对x.then属性的多次访问。这些预防措施对于确保访问者属性的一致性非常重要,因为访问者属性的值可能在不同的检索之间发生变化。

3.6

实现不应对可用链的深度设置任意限制,并假设超过该任意限制,递归将是无限的。只有真正的循环才会导致TypeError;如果遇到一个由不同的二进制数组成的无限链,则永远的递归下去是正确的行为。