本文主要是翻译 Promises/A+ 规范,然后结合自己的理解,整理成文,方便自己以后查阅
翻译
一份全面的、可互操作的 JavaScript promises 开放标准 — 由实现者编写,同样给实现者
一个 promise 表示一次异步操作的最终结果。与 promise 交互的主要方式是通过它的 then 方法,这个方法注册了2个回调函数,一个用于接收 promise 完成的最终值(value), 一个用于接收 promise 无法完成的原因(reason)。
这个规范详细说明了 then 方法的行为,提供了一个可互操作的基础,所有符合 Promises/A+ 规范的 promise 实现都可以依赖这个基础来提供。因此,这个规范应该是非常稳定的。尽管 Promises/A+ 组织可能偶尔会通过向后兼容的较小的更改来修订这份规范,以解决新发现的极端案例,只有经过非常认真的考虑、讨论、测试后我们才会集成大的或向后不兼容的改动。
从历史上看,Promises/A+ 使早期 Promises/A 提案 的行为语句更清晰易懂,扩展了它,让它涵盖了一些实际的行为,并省略了不明确或有问题的部分。
最后,这份核心的 Promises/A+ 规范并没有处理如何创建、完成或拒绝 promises , 而是选择专注在提供一个可互操作的 then 方法上。 未来,这份规范可能会涉及这些主题。
术语
- “promise” 是一个对象或者函数,有一个行为符合这份规范的
then方法。 - “thenable” 是一个对象或者函数,定义了一个
then方法。 - “value” 是任何合法的 JavaScript 值(包括 undefined 、 thenable、或者 promise )。
- "exception" 是一个使用 throw 语句作为异常被抛出的值。
- “reason” 是一个表明为什么这个 promise 会被拒绝的原因值。
要求
Promise 状态
一个 promise 的状态必须是这三种状态之一:pending (待定)、 fulfilled (完成) 或者 rejected (拒绝)。
- 当 promise 处于 pending 状态:
- 可以过渡到 fulfilled 或者 rejected 状态。
- 当 promise 处于 fulfilled 状态:
- 不可以过渡到任何状态。
- 必须有一个值(value),这个值不可以变。
- 当 promise 处于 rejected 状态:
- 不可以过渡到任何状态。
- 必须含有一个原因 (reason),这个原因不可以变。
这里的 “不可以变” 意味着不可变的标识(即,===),但并不意味着更深度的不可变。
then 方法
一个 promise 必须提供一个 then 方法来获取它当前或者最终的值(value)或原因(reason)。
一个 promise 的 then 方法接收 2 个参数:
promise.then(onFulfilled, onRejected)
-
onFulfilled和onRejected都是可选参数:- 如果
onFulfilled不是一个函数,它必须被忽略。 - 如果
onRejected不是一个函数,它必须被忽略。
- 如果
-
如果
onFulfilled是一个函数- 它必须在
promise是完成 (fulfilled) 状态之后才能被调用,promise的 value 作为它的第一个参数。 - 它不能在
promise是完成 (fulfilled) 状态之前调用。 - 它被调用的次数不能超过一次。
- 它必须在
-
如果
onRejected是一个函数- 它必须在
promise是拒绝 (rejected) 状态之后才能被调用,promise的 reason 作为它的第一个参数。 - 它不能在
promise是拒绝 (rejected) 状态之前调用。 - 它被调用的次数不能超过一次。
- 它必须在
-
onFulfilled或者onRejected不能被调用直到 执行上下文 堆栈只包含平台代码。[注:1] -
onFulfilled或者onRejected必须作为函数被调用(没有 this 值)。[注:2] -
then在同一个 promise 中可以被调用多次- 如果/当
promise是完成 (fulfilled) 状态时,所有的onFulfilled回调必须按照它们在then上创建的顺序被调用。 - 如果/当
promise是拒绝 (rejected) 状态时,所有的onRejected回调必须按照它们在then上创建的顺序被调用。
- 如果/当
-
then必须返回一个 promise [注:3]promise2 = promise1.then(onFulfilled, onRejected);- 如果
onFulfilled或者onRejected返回了一个值x, 执行 Promise 解决程序[[Resolve]](promise2,x)。 - 如果
onFulfilled或者onRejected抛出异常e, promise2 必须是 rejected 状态,e是它的reason值。 - 如果
onFulfilled不是一个方法,并且promise1是 fulfilled 状态,promise2状态也必须是 fulfilled,并且 value 值和promise1相同。 - 如果
onRejected不是一个方法,并且promise1是 rejected 状态,promise2状态也必须是 rejected,并且 reason 值和promise1相同。
- 如果
Promise 解决程序
promise 解决程序 是一个输入 promise 和 value 值的抽象操作,我们将其表示为 [[Resolve]](promise, x)。如果 x 是 thenable, 就假定 x 的行为至少有点像 promise,它将会尝试让 promise 采用 x 的状态。否则, 它将用 x 作为 value 来完成(fulfill) 这个 promise 。
对 thenables 的这种处理允许 promise 互操作来实现,只要他们暴露了符合 Promises/A+ then 方法规范。它也允许 Promises/A+ 的实现融入不符合规范但拥有合理的 then 方法。
执行 [[Resolve]](promise, x), 表现为以下步骤:
- 如果
promise和x指向的是同一个对象, 以TypeError作为原因 (reason) 拒绝这个promise。 - 如果
x是一个 promise, 采用它的状态 [注:4]- 如果
x是pending状态, 那么promise也需要保持pending状态直到x变为fulfilled或者rejected状态。 - 如果/当
x是fulfilled状态时,用相同的值使promise变为 fulfilled 状态。 - 如果/当
x是rejected状态时, 用相同的原因使promise变为 rejected 状态。
- 如果
- 除此之外, 如果
x是一个对象或者函数- 让
then赋值为x.then。 [注:5] - 如果检索属性
x.then发生错误e, 用e作为原因 (reason) 拒绝promise。 - 如果
then是一个函数,将x作为this调用它,第一个参数resolvePromise,第二个参数rejectPromise,其中:- 如果/当
resolvePromise以 valuey值被调用时,运行[[Resolve]](promise, y)。 - 如果/当
rejectPromise以 reasonr值被调用时,用r值作为原因 (reason) 拒绝promise。 - 如果
resolvePromise和rejectPromise都被调用,或者多次调用相同的参数,第一次调用被程序执行,后续的调用将会被忽略。 - 如果调用
then抛出异常e。- 如果
resolvePromise或rejectPromise已经被调用了,则忽略它。 - 否则,以
e作为原因 (reason) 拒绝promise。
- 如果
- 如果/当
- 如果
then不是对象或者函数,以x完成promise。
- 让
- 如果
x不是对象或者函数, 以x作为 value 值完成promise。
如果一个 promise 是用一个 thenable 来解决的,这个 thenable 参与了一个循环的 thenable 链,这样递归的性质 [[Resolve]](promise, thenable) 最终会导致 [[Resolve]](promise, thenable) 被再次调用,按照上面的算法会导致无限递归。鼓励实现检测这种递归并以 TypeError 作为 reason 拒绝 promise,但不是必须的。[注:6]
注
- 这里的 平台代码 指引擎、环境和 promise 实现代码。在实践中,这要求确保
onFulfilled和onRejected异步地执行,当 then 被调用,进行下一个事件循环,使用新的调用栈。这可以通过 “宏任务”机制 比如setTimeout或者setImmediate,"微任务"机制 比如MutationObserver或者process.nextTick来实现。由于 promise 的实现被认为是平台代码,在自身处理程序被调用时可能已经包含一个任务调度队列。 - 也就是说,在严格模式下,内部的
this会是undefined,在非严格模式下,this将会是全局对象。 promise1 === promise2提供的实现满足所有要求,则可以被允许。每个实现都应该记录它是否可以产生promise1 === promise2以及在什么条件下产生。- 一般来说,只有
x来自当前的实现,它才会被知道是一个真正的promise。该条款允许特定实现的使用,这里的使用意味着采用已知符合规范的 promises 的状态。 - 这个程序首先要存储
x.then的引用,然后测试这个引用,然后调用这个引用,避免多次去获取x.then属性。这些预防措施对于确保访问器属性的一致性很重要,访问器属性的值可能会在检索之间发生变化。 - 实现不应该对
thenable链的深度做任何的限制,并假设超出任何限制递归将是无限的。只有真正的死循环才会导致TypeError; 如果遇到无限循环的不同的thenable调用链,递归永远是正确的行为。
=======================================================================================
总结
这是 Promise 的实现规范,主要专注在 then 方法上。
Promise 有三种状态:pending 、fulfilled 、 rejected,并且状态是不可逆的,只能由 pending -> fulfilled 或者 pending -> rejected。
Promise 的 then 方法接收2个参数,第一个 onFulfilled,用于处理完成时的结果,第二个 onRejected , 用于处理拒绝的结果,这两个参数都应该是函数,不是函数会被忽略。
onFulfilled 和 onRejected 必须是作为下个事件循环调用(等第一批同步任务队列执行完再执行,可以用微任务实现,也可以用宏任务实现),并且只能在 pending 状态发生改变后被调用一次。then 必须作为函数被调用,不能作为对象属性的方法被调用,then 可以被调用多次,但需要保证调用的顺序与定义的顺序一致。
then 返回的必须是 promise,由于 onFulfilled 或者 onRejected 的返回值 x 不一定是一个 promise,所以需要一个处理程序来解决保证它们返回的是一个 promise ,这个程序是 [[Resolve]](promise, x)。
[[Resolve]](promise, x) 的处理逻辑是判断 x 是不是一个 promise,
如果是,使用 x 的状态作为 promise 的状态,
如果不是,还需要看 x 有没有 then 方法,
如果有,将 then 赋值为 x.then,这其中还需要根据这个 then 方法再做一层判断,then的调用或者获取出现异常就抛出异常,返回 e 为错误原因的 promise,如果可以正常符合 promise 规范的调用,并第一个回调函数被调用同样返回一个 y 值时,再递归走一遍这样的处理程序,如果调用出现异常,异常值作为原因返回这个promise。
如果没有,则返回用 x 作为完成值的 promise。
以上是大概的一个总结,细节会存在一些遗漏,主要是帮助快速理解整个规范流程。