Promise/A+ 规范
I reprint this article in Chinese, just like Prettier.
Reference:
1. Terminology
- “promise” 是一个对象或者函数,其
then
方法的实现与该规范一致。 - “thenable”是一个定义了
then
方法的对象或者函数。注意:在 ECMAScript 规范中,只要一个对象定义了
then
方法,无论实现如何,都被认为实现了 Thenable 接口。 - “value”是任意 JavaScript 值(包含
undefined
,thenable
或promise
)。 - “exception”是一个由
throw
语句抛出的值。 - “reason”是一个解释 promise 变为 rejected 原因的值。
2. Requirements
2.1 Promise States
一个 Promise 必须是以下一种状态:pending,fullfied 和 rejected.
- 当处于 pending,promise :
- 可能转变为 fullfied 或 rejected 状态。
- 当处于 fullfied,promise :
- 不能再转变为其他状态。
- 必须有一个结果值,这个值不可改变。
- 当处于 rejected,promise:
- 不能再转变为其他状态。
- 必须有一个原因,这个原因不可改变。
这里的”不可改变”意为 immutable,即 JavaScript 全等===
,但不要求深不可变(deep immutability)。
2.2 The then
Method
一个 promise 必须提供一个then
方法,以访问其当前或最终的结果值(value)或原因(reason)。
Promise 的then
方法接收两个参数:
promise.then(onFulfied, onRejected)
-
onFulfilled
和onRejected
都是可选参数:- 如果
onFulfilled
不是函数,其应该被忽略。 - 如果
onRejected
不是函数,其应该被忽略。
- 如果
-
如果
onFulfilled
是函数:- 它必须在
promise
变为 fullfied 后被调用,并以promise
的结果值(value)作为第一个参数。 - 在
promise
变为 fullfied 之前不能被调用。 - 最多只被调用一次。
- 它必须在
-
如果
onRejected
是函数:- 它必须在
promise
变为 rejected 后被调用,并以promise
的理由(reason)作为第一个参数。 - 在
promise
变为 rejected 之前不能被调用。 - 最多只被调用一次。
- 它必须在
-
onFulfilled
和onRejected
在执行上下文栈只包含平台代码(platform code)之前不可被调用。
平台代码(platform code)是指引擎、环境和 promise 实现代码。实际上,该要求保证
onFulfilled
和onRejected
在调用then
后被异步执行。这可以通过宏任务机制实现(比如setTimeout
或setImmediate
),或者微任务机制(比如MutationObserver
或process.nextTick
)。
-
onFulfilled
和onRejected
必须作为函数调用(没有this
对象)。 -
在同一个 promise 上,
then
可能被多次调用。- 当
promise
是 fullfied,所有相关的onFulfilled
必须按照其then
的调用顺序执行。 - 当
promise
是 rejected,所有相关的onRejected
必须按照其then
的调用顺序执行。
- 当
-
then
必须返回一个 promise。-
promise2 = promise1.then(onFulfied, onRejected)
-
如果
onFulfilled
或onRejected
返回了一个值x
,promise2
执行 promise pesolution procedure[[Resolve]](promise2, x)
。 -
如果
onFulfilled
或onRejected
抛出一个异常e
,promise2
转变为 rejected,其原因为e
。 -
如果
onFulfilled
不是函数,且promise1
转为了 fullfied,则promise2
转为 fullfied,其结果值(value)与promise1
一样。 -
如果
onRejected
不是函数,且promise1
转为了 rejected,则promise2
转为 rejected,其原因(reason)与promise1
一样。
-
注意: 根据具体实现的不同,
then
方法返回的 promise 可能与原 promise 完全相等。
2.3 The Promise Resolution Procedure
Promise 解析流程(promise pesolution procedure)是一个抽象操作,它接收一个 promise 和值(value)作为输入,记为[[Resolve]](promise, x)
。如果x
是一个 thenable,那么在x
与 promise 具有类似行为的估量下,promise
将采用x
的状态。否则,promise
将以x
为结果,进入 fulllfied。
对于 thenable 的这种处理,使得 promise 的实现可以互操作(interoperate),即实现了满足 Promise/A+ 规范的then
方法的对象就可以视作 promise。
运行[[Resolve]](promise, x)
,会执行以下步骤:
- 如果
promise
和x
指向同一个对象,则以TypeError
作为原因 reject 该 promise。 - 如果
x
是一个 promise,采用(adopt)其状态:- 如果
x
处于 pending,promise
保持 pending 直到x
状态改变。 - 当
x
转为 fullfied,使promise
以相同的值转为 fullfied。 - 当
x
转为 rejected,使promise
以相同的原因转为 rejected。
- 如果
- 否则,如果
x
是一个对象或函数:- 声明
then = x.then
。 - 如果
x.then
导致抛出一个异常e
,将e
作为原因 rejectpromise
。 - 如果
then
是一个函数,将x
作为其this
对象,传入第一个参数resolvePromise
和第二个参数rejectPromise
,调用then
:- 当
resolvePromise
以y
值调用时,运行[[Resolve]](promise, y)
。 - 当
rejectPromise
以原因r
调用时,用r
reject promise。 - 如果
rejectPromise
和resolvePromise
都被调用,或者一者被多次调用,只取第一次调用的结果,并忽视后续调用。 - 如果调用
then
抛出异常e
:- 如果
resolvePromise
或rejectPromise
已被调用,则忽视异常。 - 否则,以
e
为原因 reject promise。
- 如果
- 当
- 如果
then
不是函数,则以x
为值将promise
转为 fullfied。
- 声明
- 如果
x
不是函数或对象,则以x
为值将promise
转为 fullfied。(这里包括x === undefined
,即 handler 没有返回值的情况)
当上面这种实现遇到循环引用的 thenable 对象时,将进入无限递归,推荐但不要求对这种情况以TypeError
为原因 reject promise。
注意:规约中并未提到 promise 本身的状态迁移方法(即我们熟悉的 ECMAScript 中的
resolve
和reject
)该如何实现, 在 ECMAScript 中,promise 构造函数中的resolve
也会对 thenable 类型的结果值进行解包(unwrap), 所以可以大致将上面的[[Resolve]](promise, x)
视作 promise 的resolve
以及reject
方法内部需要实现的逻辑。