- 原文:https://promisesaplus.com/
一个由开发者为开发者提供的健全、互操作(interoperable)的JavaScript Promise 标准
互操作性指一种不同系统或组织之间可以协同工作的能力。在这里可以理解为只要符合该标准的的Promise实现,在语法上能够相互调用。
promise表示异步操作的最终结果。then
方法是promise主要的交互方法,它通过注册回调函数,接收promise的最终结果或promise不能被履行(fulfilled)的原因。
这份文档详细说明了then
方法的行为,提供了可互操作的基础,所有符合Promises/A+
规范的都可以以这份标准实现。因此,这份标准非常稳定。尽管Promises/A+
组织可能偶尔为了解决新发现的极端情况修改该规范,进行细微的修改,并向后兼容,我们会经过仔细考虑讨论和测试,才会整合较大的更改或向后不兼容的变更。
从过去的历史来看,Promises/A+
解释了Promise/A 提案
的早期的条款,并拓展了它,以解决早期不清楚的、有问题的部分。
最后,Promises/A+
规范的核心没有涉及如何构建,履行(fufill),拒绝(reject)承诺,而是专注于提供可互操作的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为何被拒绝原因的值。
2. 要求
2.1 Promise 状态
一个promise必须处于三种状态之一:pending
,fulfilled
,rejected
2.1.1 当pending
时,promise:
- 2.1.1.1 可以转换成
fufilled
或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
,reason
不可改变
这里的不可改变指的是(比如两个值===
),但不意味着深度不可变(比如const
声明下的对象)。
2.2 then
方法
一个promise必须提供then
方法用于接受当前或最终的值或原因。
一个promisethen
方法接受两个参数:
promise.then(onFufilled, onRejected)
2.2.1 onFufilled
,onRejected
都为可选参数
2.2.1.1 如果onFufilled
不是函数,必须被忽略
2.2.1.2 如果onRejected
不是函数,必须被忽略
2.2.2 如果onFufilled
是一个函数
- 2.2.2.1 当
promise
状态变更为fufilled
时,必须被调用,且promise
的值作为其第一个参数 - 2.2.2.2 当状态已经是
fufilled
时,不能再被调用 - 2.2.2.3 不能调用超过1次
2.2.3 如果onRejected
是一个函数
- 2.2.2.1 当
promise
状态变更为onRejected
时,必须被调用,且promise
的原因作为其第一个参数 - 2.2.2.2 当状态已经是
rejected
时,不能再被调用 - 2.2.2.3 不能调用超过1次
2.2.4 onFufilled
,onRejected
在执行环境堆栈只包含平台代码之前不得调用[3.1]
2.2.5 onFufilled
,onRejected
必须为函数形式调用,比如默认情况下函数调用时this
值为undifined
(比如fnc();
)
2.2.6 then
在同一个promise内可以调用多次
- 2.2.6.1 当
promise
状态变更为fufilled
时,所有onFufilled
回调必须按then
方法注册时的顺序依次执行 - 2.2.6.2 当
promise
状态变更为rejected
时,所有onRejected
回调必须按then
方法注册时的顺序依次执行
2.2.7 then
方法必须返回一个promise
[3.3]
promise2 = promise1.then(onFufilled, onRejected)
- 2.2.7.1 如果
onFulfilled
或onRejected
返回一个值(value)x
,那么执行[[Resolve]](promise2, x)
- 2.2.7.2 如果
onFulfilled
或onRejected
抛出一个异常e
,那么promise2
必须使用e
作为拒绝原因(promise2
捕获e
异常) - 2.2.7.3 如果
onFulfilled
不是一个函数,且promise1
状态变为fufilled
,promise2
一定会接收promise1
变更为fulfilled
时相同的值(value) - 2.2.7.4 如果
onRejected
不是一个函数,且promise1
状态变为rejected
,promise2
一定会接收promise1
变更为rejected
时相同的原因(reason)
2.3 Promise Resolution Procedure(PRP)
PRP
是一个抽象的操作,以一个promise和值(value)作为参数输入,表示为[[Resolve]](promise, x)
。如果x
是thenable
对象,假设x
的行为和promise对象有一点相似,尝试让promise
处理x
的状态。否则,x
作为值履行(fulfills)promise
。
这种处理thenables
的方式使得各种promise的实现可以互通,只要它们开放出一个与Promise/A+
协议兼容的 then 方法即可。。它也允许Promises/A+
的实现"接守"(原文"assimilate",吸收)那些不符合规范但合理的then
方法。
[[Resolve]](promise, x)
,表现为以下步骤(以下的promise
,x
):
备注:下面的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状态变更为fufilled
或者rejected
- 2.3.2.2 如果
x
的状态变更为fulfilled
,使用相同的value fufillpromise
- 2.3.2.3 如果
x
的状态变更为rejected
,使用相同的reason rejectpromise
2.3.3 其他情况,如果x
是一个对象或者函数:
- 2.3.3.1
x.then
赋值给then
[3.5] - 2.3.3.2 如果访问
x.then
会抛出异常e
,那么使用e
作为理由rejectpromise
- 2.3.3.3 如果
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
rejectpromise
- 2.3.3.3.4.1 如果
- 2.3.3.3.1 如果
- 2.3.3.4 如果
then
不是函数,以x
为值 fulfillpromise
2.3.4 如果x
不是对象或函数,以x
为值 fulfill promise
如果一个promise对象被一个处于循环thenable
链中的thenable
对象解决(resolve),由于 [[Resolve]](promise, thenable)
的递归本质会使得其再次被调用,按照上面的算法,这种情况将导致无限递归。本规范鼓励实现检测这种递归情况的出现,并使用带有一定信息的 TypeError 作为原因拒绝执行 promise [3.6],但规范不对此检测做强制要求。
3. 备注
3.1 这里“平台代码”指的是引擎(engine)、环境(environment)以及 promise 的实现代码(implementation code)。实际上,这个要求确保了onFulfilled
和onRejected
方法能够异步执行,即在调用then
方法的那个事件循环之后的新执行栈中异步执行。这个要求可以通过“宏任务(macro-task)”机制(例如 setTimeout 或者 setImmediate)或者“微任务(micro-task)”(例如MutationObserver
或者process.nextTick
)来实现。由于promise
的实现本身也被认为是“平台代码”,所以其自身可能也会包含一种任务调度队列或者“trampoline”控制结构来对其处理程序进行调用。
3.2 也就是说,this
的值,在严格模式(strict)下为undefined
;在非严格模式下为全局对象(global object)。
3.3 在满足所有要求的情况下,实现中可能允许promise2 === promise1
。每个实现都需要说明其是否允许出现promise2 === promise1
,以及在何种条件下出现。
3.4 通常来说,只有当x
是从当前实现中定义出来的,我们才知道它是否是一个真正的promise
对象。这条规则允许使用特定实现中的方法去采用符合规范的promise
对象的状态。
3.5 这一步中先会存储一个指向x.then
的引用,然后测试该调用,之后调用该引用,避免对x.then
属性的多处访问。这些预防措施对于保证访问器属性的一致性至关重要,因为其值可能在多次取值期间发生变化。
3.6 实现不应该限定thenable
链的深度,并假设只要超出了限制,递归就是无限的。只有真正的循环递归才应导致一个TypeError
错误;如果一条无限长链上的thenable对象各不相同,那么无限递归下去就是正确的行为。