Promises/A+ 规范
一个开放的,可交互的由开发者制定的 JavaScript Promise 标准。
一个 Promise 代表一个异步操作的最终结果。与 Promise 交互的主要方式是通过它的 then 方法,该 then 方法注册了两个回调函数,用来接收 Promise 的最终结果或者导致 Promise 不能 fulfilled(已完成) 的原因。
本规范详细的列出 then 方法的行为,为所有符合 Promises/A+ 规范的 Promise 提供了一个可互操作的基础来定义 then 方法。因此本规范是稳定的。尽管 Promise/A+ 组织可能会偶尔地通过一些较小的且向后兼容的修订,来解决新发现的一些边界情况。如果要进行大规模或者不兼容的更新,我们一定会经过仔细的考虑、讨论和测试。
从历史上来说,Promises/A+ 规范实际上是把之前 Promises/A规范中的建议变成了标准:扩展了原有规范约定俗成的行为,并删减了原规范的一些特例情况和有问题的部分
最后 Promises/A+ 规范不设计如何创建、解决和拒绝 promise,而是专注于提供一个可交互操作的 then 方法。未来在其他相关规范中可能会提及。
# 1、术语
- “
promise” 是一个拥有then方法的 对象或者函数,它的行为符合本规范 - “
thenable” 是一个定义了then方法的对象和函数 - 值(“
value”) 是任何JavaScript的合法值 (包括undefined,thenable, 或者promise). - 异常(“
exception”) 是使用throw抛出的一个值 - 原因(“
reason”)是一个promise的拒绝原因
# 2、要求
# 2.1、Promise 的状态
一个 Promise 的当前状态必须是以下三种状态中的一种:等待(pending),完成(fulfilled)和拒绝(rejected)
- 当一个
promise处于等待状态:- 可以迁移到完成状态或者拒绝状态中的任意一个
- 当一个
promise处于完成状态- 一定不能再迁移到其他状态
- 必须有一个值(
value),而且一定不能再改变
- 当一个
promise处于拒绝状态- 一定不能再迁移到其他状态
- 必须有一个原因(
reason),而且一定不能再改变
这里,“一定不能改变” 意味着 恒等 (例如 ===),但是并不意味着更深层次的不可变。
# 2.2、Then 方法
一个 promise 必须提供一个 then 方法,来访问它当前的最终结果或者原因
一个 promise 的 then 方法接收2个参数:
promise.then(onFulfilled,onRejected)
onFulfilled和onRejected都是可选参数:- 如果
onFulfilled不是函数,那它必须被忽略 - 如果
onRejected不是函数,那它必须被忽略
- 如果
- 如果
onFulfilled是一个函数:- 它必须在 promise 状态变成完成(
fulfilled)状态之后才会调用,并且promise的值是第一个参数 - 它一定不能在 promise 是完成(
fulfilled)状态之前被调用 - 它一定不能被调用超过1次
- 它必须在 promise 状态变成完成(
- 如果
onRejected是一个函数- 它必须在
promise状态变成拒绝(rejected)之后才会调用,并且promise的原因是第一个参数 - 它一定不能在
promise是拒绝(rejected)状态之前被调用 - 它一定不能被调用超过1次
- 它必须在
onFulfilled和onRejected只有在执行上下文栈仅包含平台代码的时候才会被调用 [3.1].onFulfilled和onRejected必须当作函数调用(例如,没有this值) [3.2]- 同一个
promise的then方法可以多次调用- 当
promise是完成(fulfilled)状态,所有相应的onFulfilled回调必须按照他们初始调用then方法的顺序执行 - 当
promise是拒绝(rejected)状态,所有相应的onFulfilled回调必须按照他们初始调用then方法的顺序执行. 7.then方法必须返回一个promise对象 [3.3].promise2 = promise1.then(onFulfilled,onRejected); - 如果
onFulfilled或者onRejected返回一个值x,则运行下面的Promise解决过程:[[Resolve]](promise2, x) - 如果
onFulfilled或者onRejected抛出一个异常e,promise2必须以e为原因被拒绝. - 如果
onFulfilled不是函数,并且promise1是完成状态,那么promise2必须以promise1同样的值完成. - 如果
onReject不是函数,并且promise1是拒绝状态,那么promise2必须以promise1同样的值拒绝.
- 当
# 2.3、Promise 解决过程
Promise 的解决过程是一个抽象操作,需要一个 promise 和一个值来作为输入,我们将其表示为[[Resolve]](promise, x)。如果 x 有 then 方法且看上去像一个 Promise ,那它会尝试让 promise 接收x的状态,否则就用x的值来完成(fulfilled) promise。
这种thenable的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。
要运行[[Resolve]](promise, x),需要执行如下步骤:
- 如果
promise和x指向同一个对象,那用TypeError为原因拒绝promise - 如果x是一个
promise,那就让promise接受x的状态 [3.4]:- 如果
x是pending状态,那promise必须保持pending状态直到x变成完成(fulfilled)或者拒绝(rejected) - 如果
x是完成态fulfilled, 那让promise用同样的值(value)完成. - 如果
x是拒绝态rejected, 那让promise用同样的原因(reason)拒绝
- 如果
x为对象或者函数,- 把
x.then赋值给then. [3.5] - 如果检索属性
x.then导致抛出了一个异常e,用e作为原因拒绝promise。 - 如果
then是一个函数,用x作为this调用它,第一个参数是resolvePromise, 第二个参数是rejectPromise:- 如果
resolvePromise被一个值y调用,执行[[Resolve]](promise, y). - 如果
rejectPromise以原因r为参数被调用,则以原因r拒绝promise - 如果
resolvePromise和rejectPromise都被调用,或者被同一参数调用了多次,则优先采用第一次调用并剩下的调用都会被忽略 - 如果调用抛出异常
e- 如果
resolvePromise或者rejectPromise已经被调用过了,那忽略它 - 用异常
e为原因拒绝promise
- 如果
- 如果
- 如果
then不是一个函数,用x完成promise
- 把
- 如果
x不是一个对象或者数组,用x完成promise
如果一个 promise 被一个循环的 thenable 链中的对象完成,而 [[Resolve]](promise, thenable)的递归性质又使得其被再次调用,根据上面的算法将会导致无限递归。规范中并没有强制要求处理这种情况,但也鼓励实现者检测这样的递归是否存在,若检测到存在则用一个可识别的 TypeError 为原因来拒绝 promise[3.6]。
# 3、注释
- 这里的平台代码是指引擎、环境以及
promise的实施代码。在实践中,要确保onFulfilled和onRejected两个参数异步执行,并且应该在then方法被调用的那一轮事件循环之后的新执行栈中执行。这可以用如setTimeout或setImmediate这样的“宏任务”机制实现,或者用如MutationObserver或process.nextTick这样的“微任务”机制实现。由于promise的实施代码本身就是平台代码,故代码自身在处理在处理程序时可能已经包含一个任务调度队列 - 严格模式下,它们中的
this将会是undefined;在非严格模式,this将会是全局对象 - 假如实现满足所有需求,可以允许
promise2 === promise1。每一个实现都应该记录是否能够产生promise2 === promise1以及什么情况下会出现promise2 === promise1 - 总的来说,只有
x来自于当前实现,才知道它是一个真正的promise。这条规则允许那些特例实现采用符合已知要求的Promise的状态 - 这个程序首先存储
x.then的引用,之后测试和调用那个引用,这样避免了多次访问x.then属性。这种预防措施确保了该属性的一致性,因为访问者属性的值可能在俩次检索之间发生变化 - 实现不应该在
thenable链的深度上做任意限制,并且假设超过那个任意限制将会无限递归。只有真正的循环才应该引发一个TypeError;如果遇到一个无限循环的thenable,永远执行递归是正确的行为