Promises/A+规范
一个开放的,可实现的,可互相操作的Javascript promise标准
1. Terminology-术语
- 1.1
"promise"是具有then方法跟随的一个对象或者函数,其行为符合规范 - 1.2
"thenable"一个可以定义跟随then方法的对象或者函数被称为 可then的(PS:不存在thenable这个词,我根据理解自己创的翻译) - 1.3
"value"可以是任何合法的JS的值(包括undefined,thenable,甚至是一个promise) - 1.4
"exception"是使用throw语句抛出的值 - 1.5
"reason"是一个value,它指示了为什么拒绝(rejected)了promise
2. Requirements-要求
2.1 Promise states-状态
一个 promise 只能有三种状态:pending,fulfilled,or rejected。
2.1.1 当处于 PENDING 状态时:
- 2.1.1.1. 将会将状态转移至fulfilled或者时rejected状态。
2.1.2 当处于 FULFILLED 状态时:
- 2.1.2.1. 不能再转移至其他状态
- 2.1.2.2. 必须有一个value,并且这个value不可改。
2.1.3 当处于 REJECTED 状态时:
- 2.1.3.1. 不能再转移至其他状态
- 2.1.3.2. 必须有一个reason,并且这个reason不可改。 (PS:这里的"不可改"指的是指向不可改例如(i.e ===),但不是深层的不可改,看下面代码:)
resolve(value);
reject(reason);
value 和 reason 写进来不可变。
value = {
a: 1,
b: {
c:3
}
}
a = value;//此处之后修改a,value内部也会变。
a === value;//只要value的引用没有变化,则仍旧会通过。
//即value内部的值可以改变,但是value这个引用不能变了。
2.2 then 方法
一个promise必须提供一个then方法来访问它当前的或者最终的value或者reason。
then方法接收两个参数:
promise.then(onFulfilled, onRejected)
.2.1 onFulfilled和onRejected都是可选参数。
2.2.1.1 如果onFulfilled不是一个方法,他会被忽略
2.2.1.2 如果onRejected不是一个方法,他会被忽略
2.2.2 如果onFulfilled是一个方法
2.2.2.1 他将会在promise是`fulfilled`状态时被调用,并且将promise的`value`作为他的第一个参数。
2.2.2.2 在promise没有转至`fulfilled`前它不会被调用。
2.2.2.3 它不能被多次调用。
2.2.3 如果onRejected是一个方法
2.2.2.1 他将会在promise是`rejected`状态时被调用,并且将promise的`reason`作为他的第一个参数。
2.2.2.2 在promise没有转至`rejected`前它不会被调用。
2.2.2.3 它不能被多次调用。
2.2.4 在执行上下文堆栈里仅包含平台代码之前,不得调用onFulfilled或者onRejected。(3.1)(PS:这里的加粗字体里的执行是名词,[执行上下文堆栈]是个名字)
2.2.5 onFulfilled和onRejected必须作为函数调用(即没有此值)。
2.2.6 then 方法可以被同样的promise多次调用。
2.2.6.1 如果promise是fulfilled态的,则它们所有的、各自的onfulfilled函数回调必须按照他们的原始调用顺序执行。
2.2.6.2 如果promise是rejected态的,则它们所有的、各自的onrejected函数回调必须按照他们的原始调用顺序执行。
2.2.7 then 必须返回一个promise(3.3)
promise2 = promise1.then(onfulfilled,onrejected);
2.2.7.1 如果onFulfilled或onRejected返回一个value x,那么运行promise 解决程序,`[[resolve]](promise2,x)`.(2.3)
2.2.7.2 如果onFulfilled或onRejected抛出异常e,promise2必须用e作为reason来rejected
var a = new Promise((resolve, reject) => {
resolve(1);
})
a.then(() => {
throw new Error ('error');
})
.then(
value => {
console.log('then->funfilled->1',value);
},
reason => {
console.log('then->rejected->1',reason);
}
).then(
value => {
console.log('then->funfilled->2',value);
},
reason => {
console.log('then->rejected->2',reason);
}
);
/*
then-rejected-1 Error: error
at p.then (<anonymous>:5:11)
init.js:1 then->funfilled->2 undefined
*/
2.2.7.3 如果onfulfilled不是一个方法并且promise1是fulfilled态,那么promise2 就必须用和promise1相同的value去fulfilled。
```
var a = new Promise((resolve, reject) => {
resolve(1); }) a.then() .then(value => { console.log(value); });
// 1 ```
2.2.7.4 如果onrejected不是一个方法并且promise1是rejected态,那么promise2 就必须用和promise1相同的reason去rejected。
var a = new Promise((resolve, reject) => {
resolve(1);
})
a
.then(() => {
throw new Error ('error');;
})
.then()
.then(value => {
console.log(value);
},error => {
console.log(error);
});
/*
Error: error
at a.then (<anonymous>:6:11)
*/
2.3 The Promise Resolution Procedure-
Promise Resolution Procedure是一个将promise和value作为输入的抽象操作,我们将其表示为[[Resolve]](promise,x).如果x是可then的(thenable),则在x的行为至少类似于promise的假设下(我认为就是说将x的操作也看成promise),尝试使promise采用x的状态,否则会以值x来fulfills promise。
这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。
运行 [[Resolve]](promise, x) 需遵循以下步骤:
2.3.1 如果promise和x指向同一对象,以TypeError为据因拒绝执行promise
var p = new Promise( (resolve)=> {
setTimeout( ()=> {
resolve(p);
//promise和x指向同一对象
}, 0)
});
2.3.2 如果 x 为 Promise ,则使 promise 接受 x 的状态
2.3.2.1 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
2.3.2.2 如果 x 执行完毕,用相同的value执行 promise
2.3.2.3 如果 x 处于拒绝态,用相同的据因拒绝 promise
```
var p = new Promise((resolve, reject) => {
var p2 = new Promise((resolve, reject) => {
resolve(22)
})
resolve(p2)
}).then((n) => {
console.log(n)
})//22
```
2.3.3 如果 x 为对象或者函数:
2.3.3.1、把x.then赋值给then.【注5】
2.3.3.2、如果取 x.then 的值时抛出错误 e ,则以 e 为reason拒绝 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.4、如果 then 不是函数,以 x 为参数执行 promise
2.3.4 如果 x 不为对象或者函数,以 x 为参数执行 promise
如果一个 promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为reason来拒绝 promise 。
3. Notes 备注
3.1 这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保onFulfilled和onRejected方法异步执行,且应该在then方法被调用的那一轮事件循环之后的新执行栈中执行.这个事件队列可以采用“宏任务(macro-task)”机制(setTimeout, setImmediate,)或者“微任务(micro-task)”机制(MutationObserver , process.nextTick)来实现。由于 promise 的实施代码本身就是平台代码(我认为就是由js编写的),故代码自身在处理在处理程序时可能已经包含一个任务调度队列。
3.2 在严格模式(strict)中,函数this的值为undefined;在非严格模式中其为全局对象。
3.3 代码实现在满足所有要求的情况下可以允许promise2 === promise1。每个实现都要文档说明其是否允许以及在何种条件下允许promise2 === promise1。
3.4 总体来说,如果x符合当前实现,我们才认为它是真正的promise。这一规则允许那些特例实现接受符合已知要求的 Promises 状态。
3.5 这步我们先是存储了一个指向x.then的引用,然后测试并调用该引用,以避免多次访问x.then属性。这种预防措施确保了该属性的一致性,因为其值可能在检索调用时被改变。
3.6 实现不应该对thenable链的深度设限,并假定超出本限制的递归就是无限循环。只有真正的循环递归才应能导致TypeError异常;如果一条无限长的链上thenable均不相同,那么递归下去永远是正确的行为。
PS
本人英语功底实属不行,欢迎各位指出其中的不足,交流。