本博客 hjy-xh,转载请申明出处
一个开放标准,通用的 JavaScript promise ,由开发者制定,供开发者使用。
Promise 用于表示一个异步操作的最终结果。与 Promise 交互的主要方式为then方法,该方法注册回调函数以接收 promise 的成功的结果或者失败的原因。
该规范详细说明了then方法的行为,所有基于 Promise/A+规范实现的 Promise 都必须以此为基础实现。因此规范应该非常稳定。尽管 Promises/A+组织可能会偶尔修改此规范以向后兼容,只有经过仔细考虑,讨论和测试后,我们才会集成大型或不向后兼容的更改。
从其发展过程来看,Promises/A+其实是把之前的Promises/A提案归纳总结为标准。扩展了一些现有的行为规范,并删除了一些有问题的、未明确的部分。
最终,Promises/A+规范核心内容不包括怎样处理 promises 的创建(create),完成(fulfill)和拒绝(reject),而是选择专注于提供一个通用的,具备可互操作的then方法。上述的操作方法可能会在未来修改该规范时提及。
1. 术语
1.1 “promise” 是一个拥有then方法的对象或函数,其行为符合本规范
1.2 “thenable” 是一个定义了then方法的对象或函数
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 当 promise 处于 pending 状态
2.1.1.1. 可以转换到 fulfilled 或 rejected 的状态。
2.1.2 当 promise 处于 fulfilled 状态
2.1.2.1 不能再转换状态。
2.1.2.2 必须有一个值(value),且不可改变。
2.1.3 当 promise 处于 rejected 状态
2.1.3.1 不能再转换状态。
2.1.3.2 必须有一个原因(reason),且不可改变。
这里的不可改变指的是恒等(即 === ),而不是意味着其内部的不可变(即仅仅是其引用地址不变,但属性值可被更改)。
2.2 then 方法
一个 promise 必须提供一个then方法以读取其当前值、终值和失败原因。
一个 promise 的then方法接收两个参数:
1 | promise.then(onFullfilled, onRejected); |
2.2.1 onFulfilled和onRejected都是可选的参数:
2.2.1.1 如果
onFullfilled不是一个函数,则它会被忽略2.2.1.2 如果
onRejected不是一个函数,则它会被忽略2.2.2 如果
onFulfilled是一个函数2.2.2.1 必须在
promise执行结束后执行,promise的 value 作为第一个参数2.2.2.2 在
promise执行结束前不可被调用2.2.2.3 不能被多次调用
2.2.3 如果
onRejected是一个函数2.2.3.1 必须在
promise被拒绝后执行,promise的 reason 作为第一个参数2.2.3.2 在
promise执行结束前不可被调用2.2.3.3 不能被多次调用
2.2.4
onFulfilled和onRejected只有在执行环境堆栈仅包含平台代码时才可被调用 3.12.2.5
onFulfilled和onRejected必须被作为函数调用(即没有 this 值) 3.22.2.6
then方法可以被同一个 promise 调用多次2.2.6.1 如果/当
promise是 fulfilled 状态,则所有相应的onFulfilled回调必须按注册顺序执行then方法2.2.6.2 如果/当
promise是 rejected 状态,则所有相应的onRejected回调必须按注册顺序执行then方法2.2.7
then方法必须返回一个 promise 对象3.3
1 | promise2 = promise1.then(onFulfilled, onRejected); |
- 2.2.7.1 如果
onFulfilled或者onRejected返回一个值x,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x) - 2.2.7.2 如果
onFulfilled或者onRejected抛出一个异常e,则promise2必须拒绝执行,并将e作为拒绝原因 - 2.2.7.3 如果
onFulfilled不是一个函数并且promise1已经完成,则promise2必须使用与promise1相同的 value 来 fulfilled - 2.2.7.4 如果
onRejected不是一个函数并且promise1已经完成,则promise2必须使用与promise1相同的 reason 来 rejected
2.3 Promise 解决过程
Promise 解决过程是一个抽象的操作,其需输入一个 promise 和一个值,我们表示为[[Resolve]](promise, x),如果 x 是一个 thenable(promise),它会尝试采用 x 的状态,前提是x行为至少有点像 promise。否则,它作为promise的 fulfilled 的 value 返回。
对 thenables 的这种处理允许 promise 实现进行更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与不冲突的实现能良好共存。
要运行[[Resolve]](promise, x), 需遵循以下步骤:
- 2.3.1 如果
promise和x指向同一对象,以TypeError为拒绝原因拒绝执行promise - 2.3.2 如果
x为 Promise,则使 promise 接受 x 的状态3.4- 2.3.2.1 如果
x处于 pending,promise 需保持 pending,直至x被执行或拒绝 - 2.3.2.2 如果
x执行完毕,用相同的值执行promise - 2.3.2.3 如果
x被拒绝,用相同的原因拒绝promise
- 2.3.2.1 如果
- 2.3.3 如果
x为对象或者函数- 2.3.3.1 把
x.then赋值给then3.5 - 2.3.3.2 如果取
x.then的值时抛出错误e,则用e作为 promise 的拒绝原因 - 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为原因拒绝promise
- 2.3.3.3.4.1 如果
- 2.3.3.3.1 如果
- 2.3.3.4 如果
then不是函数,以x为参数执行promise
- 2.3.3.1 把
- 2.3.4 如果
x不为对象或者函数,以x为参数执行promise
如果一个 promise 被一个循环的 thenable 链中的对象解决,而[[Resolve]](promise, thenable)的递归性质导致其被再次调用,根据上述的算法将会陷入无限递归。规范鼓励施者检测这样的递归是否存在,但不强制,如果检测到存在则以一个可识别的TypeError为原因来拒绝 promise。3.6
3. 备注
3.1
这里的“平台代码”指的是引擎,环境和 promise 实现代码。实践中要确保onFulfilled和onRejected方法异步执行,且应该在then方法被调用的那一轮事件循环之后的新执行栈中执行。这可以用“宏任务”机制实现,例如setTimeout或者setImmediate,或者用“微任务”机制,例如MutationObserver或process.nextTick。由于 promise 实现被认为是平台代码,因此它本身可能包含一个任务调度队列或“trampoline”的处理程序。
3.2
也就是说在严格模式(strict)中,this的值为undefined;在非严格模式中其为全局对象。
3.3
代码实现在满足所有要求的情况下可以允许promise2 === promise1。每个实现都要文档说明其是否允许以及在何种条件下允许 promise2 === promise1。
3.4
一般来说,x如果它来自当前的实现,那么它是一个真正的 promise。该子句允许使用特定于实现的方法来采用已知符合的 promise 的状态。
3.5
此过程首先存储引用x.then,然后测试,调用该引用,避免多次访问该x.then属性。这么做的原因是防止每次获取该值时,返回不同的情况(ES5 的 getter 特性可能会产生副作用)
3.6
实现不应该对 thenable 链的深度设限,并假定超出本限制的递归就是无限循环。只有真正的循环递归才应能导致TypeError异常;如果一条无限长的链上 thenable 均不相同,那么递归下去永远是正确的行为。