本文翻译基于 Promises/A+ 官网原文,其格式和 Github 地址原文 略有不同。若有纰漏还望指出。
一份通用可交互操作的 JavaScript promises 开放标准,由实现者制定,供实现者参考。
promise
对象代表异步操作的最终结果。与 promise
对象交互的主要方式是通过它的 then
方法,用该方法注册回调函数,可以接收 promise
完成的最终值 value
或不能完成的原因 reason
。
本规范详细定义了 then
方法的行为,为所有遵循 promise/A+
规范的方案提供了一份实现基础。因此,本规范是非常稳定的。Promises/A+
组织可能为解决一些新发现的边界情况,而对本规范进行微小的向后兼容修正,但是只有经过仔细考虑、讨论和测试,我们才会加入较大的、不兼容的改动。
从历史上看,Promises/A+
澄清了早期 Promises/A
建议的行为条款,并扩展它以覆盖 事实上 的行为,移除其中未指定或有问题的部分。
最后,核心 Promises/A+
规范并不处理如何创建、实现或拒绝 promises
,而是专注于提供一种可交互操作的 then
方法规范。未来和规范相关的工作可能会涉及这些主题。
1.1 "promise"
是拥有 then
方法,行为遵循本规范的对象或函数。
1.2 "thenable"
是定义了 then
方法的对象或函数。
1.3 "value"
是任何合法的 JavaScript 值(包括 undefined
,thenable
以及 promise
)。
1.4 "exception"
是使用 throw
语句抛出的值。
1.5 "reason"
代表 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 必须拥有一个固定不变的值
value
。
2.1.3 拒绝状态 rejected
时,promise
对象:
-
2.1.3.1 不能转变为任何其它状态。
-
2.1.3.2 必须拥有一个固定不变的原因
reason
。
在这里,“固定不变”是指恒等标识符(例如:===
),但并不代表深层的值固定不变。
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
对象完成fulfilled
后必须对其调用,并且将promise
对象的最终值value
作为其第一个参数。 -
2.2.2.2
promise
对象完成fulfilled
前,不能对其调用。 -
2.2.2.3 对其调用次数不能超过一次。
2.2.3 如果 onRejected
是一个函数:
-
2.2.3.1
promise
对象拒绝rejected
后必须对其调用,并且将promise
对象的原因reason
作为其第一个参数。 -
2.2.3.2
promise
对象拒绝rejected
前,不能对其调用。 -
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
的顺序依次执行。 -
2.2.6.2 如果/当
promise
对象拒绝后,所有onRejected
回调函数,必须按照其初始调用then
的顺序依次执行。
2.2.7 then
方法必须返回一个 promise
对象 [3.3]。
promise2 = promise1.then(onFulfilled, onRejected)
-
2.2.7.1 如果
onFulfilled
或onRejected
返回值为x
,运行Promise Resolution Procedure
[[Resolve]](promise2, x)
函数。 -
2.2.7.2 如果
onFulfilled
或onRejected
抛出异常e
,则promise2
必须拒绝,并以e
作为拒绝原因。 -
2.2.7.3 如果
onFulfilled
不是函数,且promise1
已完成,promise2
必须完成并返回相同的值value
。 -
2.2.7.4 如果
onRejected
不是函数,且promise1
已拒绝,promise2
必须拒绝并返回相同的原因reason
。
Promise Resolution Procedure
是一个抽象的操作,接收一个 promise
对象和一个值 value
作为参数,我们将其表示为 [[Resolve]](promise, x)
。如果 x
是一个 thenable
对象,且其行为类似于 promise
对象,PRP
就会尝试让 promise
接收 x
的状态;否则就以 x
完成 promise
。
这种处理 thenables
对象的方式使得各种 promise
的实现可以互通操作,只要它们暴露一个 Promise/A+
协议兼容的 then
方法即可。这也使得遵循 Promise/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
必须保持等待状态pending
直到x
完成fulfilled
或 拒绝rejected
。 -
2.3.2.2 如果/当
x
已完成fulfilled
,则使用相同的值完成promise
。 -
2.3.2.3 如果/当
x
已拒绝rejected
,则使用相同的原因拒绝promise
。
-
-
2.3.3 否则,如果
x
是对象或函数:-
2.3.3.1 将变量
then
赋值为x.then
[3.5]。 -
2.3.3.2 如果获取
x.then
的结果时抛出异常e
,则使用e
作为原因拒绝promise
。 -
2.3.3.3 如果
then
是函数,则以this
为x
的上下文进行调用,以resolvePromise
为第一个参数,rejectPromise
为第二个参数,其中:-
2.3.3.3.1 如果/当以值
y
为参数调用resolvePromise
时,则运行[[Resolve]](promise, y)
。 -
2.3.3.3.2 如果/当以原因
r
为参数调用rejectPromise
时,则使用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.4 如果
then
不是函数,以x
为值完成promise
。
-
-
2.3.4 如果
x
不是对象或函数,以x
为值完成promise
。
如果 promise
对象被一个处于循环的 thenable chain
中的 thenable
对象完成 resolved
,由于 [[Resolve]](promise, thenable)
递归的本性会使得其再次被调用,按照上面的算法,这种情况将导致无限递归。本规范鼓励实现者检测这种递归情况的出现,并使用带有一定信息的 TypeError
作为原因拒绝 promise
[3.6],但不作强制要求。
-
3.1 这里的“平台代码” 是指引擎、环境以及
promise
的实现代码。在实践中,要求确保onFulfilled
和onRejected
方法能够异步执行,即在调用then
方法的那个事件循环event loop
之后的新执行栈中异步执行。可以通过“宏任务”机制如:setTimeout
、setImmediate
或“微任务”机制如:MutationObserver
、process.nextTick
来实现。既然promise
的实现本身也被认为是“平台代码”,所以其本身也应包含一个任务调度队列或“trampoline”来调用其中的处理程序。 -
3.2 也就是说,
this
的值在严格模式下为undefined
;在非严格模式下为全局对象global object
。 -
3.3 在满足所有要求的情况下,实现中允许出现
promise2 === promise1
的情况。每种实现都应该说明其是否允许出现promise2 === promise1
,以及在什么条件下允许出现。 -
3.4 通常来讲,只有当
x
是从当前实现中定义出来的,我们才知道它是一个真正的promise
对象。这条规则使得基于特定实现的方法可以接收符合规范的promises
的状态。 -
3.5 在此步骤中,首先存储一个指向
x.then
的引用,再检测该引用,最后调用该引用,避免x.then
属性被多次访问调用。这些预防措施对于确保访问器属性的一致性非常重要,因为其值可能在多次取值期间发生变化。 -
3.6 本规范的实现 不应该 限定
thenable chains
的深度,假设超出任意限定,递归将是无限的。只有真正的循环才会导致TypeError
;如果遇到一条长度无限且thenable
对象均不相同的chain
,无限递归即是正确的行为。