ISSUE / 008
[译]Promises/A+ 标准
Promises/A+ 标准 一个可靠的、可交互的 JavaScript Promises 开放标准 ——供实现者参考 一个 promise 表示一个异步操作的最终结果。和 promise 交互的主要方式是通过它的 then 方法,通过在 then 方法中注册回调函数来接收 promise 的最终值或者…
![[译]Promises/A+ 标准的文章题图](https://user-images.githubusercontent.com/19587016/52756088-36141400-303b-11e9-87ef-38710a554f71.png)
Promises/A+ 标准

一个可靠的、可交互的 JavaScript Promises 开放标准 ——供实现者参考
一个 promise 表示一个异步操作的最终结果。和 promise 交互的主要方式是通过它的 then 方法,通过在 then 方法中注册回调函数来接收 promise 的最终值或者无法实现的原因(拒因)。
本规范详尽描述了 then 方法的行为,为所有 Promises/A+ 的实现提供了一个可交互的基础。因此,该规范将是非常稳定的。尽管 Promises/A+ 组织 可能会偶尔通过修正(向后兼容的 minor 版本)该规范来解决一些新发现的边界情况。只有经过仔细的考虑、讨论和测试之后,我们才会整合出一个大的或向后不兼容的版本。
Promises/A+ 澄清了早期的 Promises/A 提案的行为条款,扩展了一些内容并省略了未明确说明或有问题的部分。
最后,核心的 Promises/A+ 规范不涉及如何create(创建)、fulfill(履行)、reject(拒绝) promises,而是选择专注于提供一种可交互的 then 方法。未来在配套规范中的工作可能会涉及这些主题。
1. 名词解释
- 1.1. "promise" 是一个拥有符合本规范行为描述的
then方法的object或者function。 - 1.2. "thenable" 是一个定义了
then方法的object或者function。 - 1.3. "value" 是一个 JavaScript 的合法值(包括
undefined,thenable或者promise) - 1.4. "exception" 是一个通过
throw语句抛出的值。 - 1.5. "reason" 是一个值,用来表示拒绝
promise的原因。
译者注:
promise和thenable很像,区别就在于then方法是否严格符合本规范的定义,例如:const obj = { then() { console.log("I'm not a promise") } }只能称
obj是一个thenable而不能称其为promise,因为它的then方法不符合本规范的行为描述。reason在之后都称之为为拒因。
2. 要求
2.1 Promise 状态
一个 promise 的状态必须是 pending、fulfilled、rejected三种状态中的一种。
2.1.1 当
promise处于pending状态时:- 2.1.1.1
promise可以转变为fulfilled或者rejected状态。
- 2.1.1.1
2.1.2 当
promise处于fulfilled状态时:- 2.1.2.1
promise禁止转变为其他状态。 - 2.1.2.2 必须拥有一个不可变的值。
- 2.1.2.1
2.1.3 当
promise处于rejected状态时:- 2.1.3.1
promise禁止转变为其他状态。 - 2.1.3.2 必须拥有一个不可变的拒因
- 2.1.3.1
注意:不可变 意味着恒等(即 ===),但不意味着深度不可变。
译者注:当值或者拒因为为引用类型时,只要其引用地址相同即可。
2.2 then 方法
一个 promise 必须提供一种方法来访问其当前的最终值或者拒因。
一个 promise 的 then 方法接收两个参数:
promise.then(onFulfilled, onRejected)
2.2.1
onFulfilled和onRejected都是可选参数:- 2.2.1.1 如果
onFulfilled不是一个函数,则必须忽略掉。 - 2.2.1.2 如果
onRejected不是一个函数,则必须忽略掉。
- 2.2.1.1 如果
2.2.2 如果
onFulfilled是一个函数:- 2.2.2.1 只能在
promise的状态为fulfilled时被调用,并且第一个参数是promise的终值 - 2.2.2.2 不能在
promise状态为fulfilled之前被调用 - 2.2.2.3 只能被调用一次
- 2.2.2.1 只能在
2.2.3 如果
onRejected是一个函数:- 2.2.3.1 只能在
promise的状态为rejected时被调用,并且第一个参数是promise的拒因 - 2.2.3.2 不能在
promise状态为rejected之前被调用 - 2.2.3.3 只能被调用一次
- 2.2.3.1 只能在
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为fulfilled状态时,所有接收到的onFulfilled回调函数必须按照其调用then的顺序执行。 - 2.2.6.2 当
promise为rejected状态时,所有接收到的onRejected回调函数必须按照其调用then的顺序执行。
- 2.2.6.1 当
2.2.7
then方法必须返回一个promise。[3.3]promise2 = promise1.then(onFulfilled, onRejected);- 2.2.7.1 如果
onFulfilled或者onRejected返回了一个值x,则执行 Promise 决策程序[[Resolve]](promise2, x) - 2.2.7.2 如果
onFulfilled或者onRejected返回了一个异常e,则promise2必须转化为rejected状态并以e作为拒因。 - 2.2.7.3 如果
onFulfilled不是一个函数并且promise1是fulfilled状态,则promise2也必须是fulfilled状态并返回相同的值(promise1返回的值)。 - 2.2.7.4 如果
onRejected不是一个函数并且promise1是rejected状态,则则promise2也必须是rejected状态并返回相同的拒因(promise1返回的拒因)。
- 2.2.7.1 如果
2.3 Promise 决策程序
Promise 决策程序(promise resolution procedure) 是一个抽象的操作,将 promise 和值作为输入,用代码表示为 [[Resolve]](promise, x)。如果 x 是一个 thenable,则该程序会尝试让 promise 接受 x 的状态,我们假设 x 的行为至少像一个 promise。 否则 promise 则以 x 为最终值直接执行。
这种对 thenable 的处理使得 promise 的实现更加灵活,只要暴露出符合Promises/A+的规范的 then 方法即可。它还允许 Promises/A+兼容不合规范的实现。
运行[[Resolve]](promise, x),将执行以下几种步骤:
2.3.1 如果
promise和x引用相同的对象,则拒绝执行promise并且以TypeError为拒因。2.3.2 如果
x是一个promise,则接受它的状态[3.4]:- 2.3.2.1 如果
x处于pending状态,则promise则必须保持pending状态直到x变为fulfilled或者rejected状态。 - 2.3.2.2 如果
x处于fulfilled状态,则以相同的值执行promise。 - 2.3.2.3 如果
x处于rejected状态,则以相同的拒因拒绝执行promise。
- 2.3.2.1 如果
- 2.3.3 否则,如果
x是一个对象或者函数,- 2.3.3.1 使用
x.then作为promise的then。[3.5] - 2.3.3.2 如果获取
x.then的过程中发生了异常e,则以e作为拒因拒绝执行promise。 - 2.3.3.3 如果
x.then是一个函数,则以x作为this值调用该函数,第一个参数是resolvePromise, 第二个参数是rejectPromise,其中:- 2.3.3.3.1 如果
resolvePromise以值y为参数被调用,则执行[[Resolve]](promise, y)。 - 2.3.3.3.2 如果
rejectPromise以拒因r为参数被调用,则以r为拒因拒绝执行promise。 - 2.3.3.3.3 如果
resolvePromise和rejectPromise都被调用,或者以同样的参数调用多次,则采用第一次调用并且忽略掉其他的调用。 - 2.3.3.3.4 如果调用
then方法中发生异常e,- 2.3.3.3.4.1 如果
resolvePromise或者rejectPromise被调用,则忽略它。 - 2.3.3.3.4.2 否则以
e为拒因拒绝执行promise。
- 2.3.3.3.4.1 如果
- 2.3.3.3.1 如果
- 2.3.3.4 如果
x.then不是一个函数,则以x为最终的值执行promise。
- 2.3.3.1 使用
- 2.3.4 如果
x不是一个对象或者函数,则以x为最终的值执行promise。
如果一个 promise 在一条 由 thenable 组成的循环链中被 resolved,则会导致[[Resolve]](promise, thenable) 执行 [[Resolve]](promise, thenable),造成无限循环。我们鼓励去检测由此导致的错误,并以 TypeError 拒绝执行 promise,但这不是必须的。[3.6]
附录
3.1
这里的 platofrm code 是指引擎、环境和 promise 实现代码。实际上,这一要求确保 onFulfilled 和 onRejected 是异步执行,并且是在 then 所被调用的那一轮的事件循环之后的新执行栈中执行。这边可以使用 setTimeout 或者 setImmediate 这类的宏任务(macro-task)实现或者使用 MutationObserver 或 process.nextTick 这类的微任务(micro-task)去实现。因为promise的实现被认定为平台代码,他可能包含自身的任务队列。
3.2
也就是说,在严格模式下,this 值可能会是undefined,在非严格模式下,this 指向全局对象。
3.3
允许实现 promise2 === promise1,前提是必须实现所有要求。并且每个实现都应该记录它是否会产生及在什么条件下会产生 promise2 === promise1。
3.4
只有符合当前规范的 x 才能被称为真正的 promise
3.5
首先存储对 x.then 的引用,然后测试该引用,然后调用该引用的过程应该避免对 x.then 属性的多次访问。此类预防措施对于确保访问属性的一致性非常重要,访问属性的值可能会在检索过程中发生变化。
3.6
实现不应该去设置 thenable 链的最大长度,并且去假定调用超过了设置的最大长度就是无限循环的调用。只有真正的递归调用才会抛出 TypeError,如果 thenable 链上的每一个 thenable 对象都不相同,那他就应该无限调用下去。
