前言:必要前提
阅读本文有一个很重要的前提—— 就是知道 Promise是怎么构造的,也可以说知道在构造Promise实例过程中究竟发生了什么,是理解Promise必不可缺的关键一步。
试着问下自己这几个问题:
- Promise的接收的回调函数是怎么执行的?(MDN称之为
executor) resolve和reject是什么? 是谁提供的?- 返回的
Promise是怎么确定状态的?
如果回答这些问题没有把握,可以参考:
- MDN Promise
- Promise简介
- 其他博文:尽可能是 github上的,分析得头头是道、有理有据,但有些难理解就是了。
如果上面的问题都能很好的回答上来,那么就正式开始用Promise/A+规范解读Promise(虽说如此,还是要结合MDN 和 tutorialpoint上的内容)。
开始吧。
预备:Promise基本构造(非规范)
注意:
- 这不是
Promise/A+的一部分,但是知道它对于理解Promise规范的细节很有帮助。 - 下面的内容我会在括号中引用,或是在伪代码中引用。
任何一个Promise都会有两个最关键的部分:
[[PromiseStatus]]: 表明操作执行后的状态。默认为pending; 在Firefox浏览器中,该字段为state[[PromiseValue]]:是保存在Promise中的结果,它可能是一个reason(拒绝原因,异常.etc); 或是一个value(成功操作后提供的结果)。
关于[[PromiseStatus]]和[[PromiseValue]]的设置:
- 在调用
resolve函数时,如果提供了参数y; 那么[[PromiseValue]]则被设为y的值。此时[[PromiseStatus]]将切换为fulfilled(或resolved) ; 例如:resolve(y) - 在调用
reject函数时,如果提供了异常或一个值r, 那么[[PromiseValue]]将被设为r。它可能是一个异常(Exception)。此时[[PromiseStatus]]将切换为rejected。 例如:reject(r)或throw r
一旦[[PromiseStatus]]被设为fulfilled或rejected二者任何一个状态,都可以说该Promise被settled。
总结如下:
任何一个Promise实例pr的value或reason都能表示为:
pr . [[PromiseValue]] // 它可能是value ,也可能是 reason。
任何一个Promise实例pr的状态都能表示为:
pr . [[PromiseStatus]] // pending / fulfilled / rejected 三者之一。
Promise 与 then方法
注意:
- 参考来源: Promise/A+规范
- 虽然是参考规范,但是并非是规范的翻译,小生也没有翻译规范的能力;下文大多是阅读规范后的理解。
Promise/A+规范规定,Promise必须为以下三种状态之一:
pending: 操作还未确定结果。此时promise状态(即[[PromiseStatus]])可以切换到fulfilled或rejectedfulfilled:操作已经成功完成。无法再切换到其他状态。此时必须有一个确定的value(即[[PromiseValue]]无法再更改)rejected:操作已经失败。无法再切换到其他状态。此时必须有一个确定的reason(即[[PromiseValue]]无法再更改)
任何一个promise实例都必须有一个then方法;同样的,任何具有then方法的对象都可以称之为thenable对象。
如下所示:
promise.then(onFulfilled, onRejected)
onFulfilled与onRejected都是可选的,但是它们如果不是函数,都会被忽略。注意它们只能在promise被settled后调用并且只能被调用一次。onFulfilled是一个函数时,promise的状态为fulfilled时被调用。并将promise的value(即[[PromiseValue]])作为onFulfilled的第一个参数。onRejected是一个函数时,promise的状态为rejected状态时被调用,并且promise的reason(也是[[PromiseValue]]) 作为它的第一个参数。
注意:
- 只有在当前任务(即宏任务)完成后才能调用
onFulfilled与onRejected回调;如果当前宏任务未执行完毕,那么它们都不能被调用。 这保证了then是异步的。 - 一个
then方法可以被同一个promise调用多次。
写一个伪代码:
promise.then((value)=>{ // onFulfilled callback
// [[PromiseStatus]] === fulfilled
// [[PromiseValue]] === value
}, (reason)=>{ // onRejected callback
// [[PromiseStatus]] === rejected
// [[PromiseValue]] === reason
})
then方法必须返回一个Promise实例
在知道怎么将返回值转换为promise前,必须知道[[Resolve]](有大佬译作promise解决程序,我就借用了)
Promise解决程序[[Resolve]]
[[Resolve]]是一个抽象操作,即:将x转换为Promise实例promise1的过程记作[[Resolve]](promise1,x)。这里x可以是任何类型的值,如undefined、primitive data(原始值)、thenable对象等等。
伪代码如下:
x , promise1 // x 是任何值。
[[Resolve]](promise1, x) // 执行Promise解决程序, x 和 promise作为参数
promise1 // 是根据值x转换得到的Promise实例值。
注意:
- 再次重申, thenable是拥有
then方法的对象。 - 为了防止混淆, 我会将
Promise实例称作promise1,而不是规范中的promise。 - 过程我会排除掉一些少见情形,例如
x和promise1是相同时,会抛出TypeError错误等等。必要时我会带上。
[[Resolve ]](promise1,x) 过程如下:
PA:
- 假定:
x是一个Promise实例,那么:
- 如果
x处于pending状态,那么promise1状态也将为pending; 直至x切换到fulfilled或rejected状态为止。 - 如果
x处于fulfilled状态,promise1也将切换到fulfilled状态,并将promise1的value(即promise1.[[PromiseValue]],下同)也设为x的value - 如果
x处于rejected状态,promise1也将切换到rejected状态,并将promise1的reason(也是promise1.[[PromiseValue]],下同)也设为x的reason
PB :
- 假定:
x不可能是Promise实例; - 假定:
x是一个对象或函数。 - 假设
then为x.then;即then = x.then;
- 若取属性
x.then抛出异常er, 那么将promise1的reason设为er,并将promise1的状态(即promise1 . [[PromiseStatus]],下同)设为rejected - 如果
then是一个函数,那么进入PC; 此时x为thenable对象 - 如果
then不是一个函数 (注意:x也是一个对象或函数), 此时x不是thenable对象; 那么将promise1的状态设为fulfilled,并将promise1的value设为值x。
PC:
- 此时
x必须是一个thenable对象 - 假定
then是x.then - 假定
x对象定义如下:
x = {
....
then(resolvePromise, rejectPromise){ // resolvePromise, rejectPromise 是回调函数
...
...
}
}
- 将
then方法的this指向x,并调用then方法; 注意then方法接收两个回调函数。 - 如果在调用
resolvePromise时提供了一个参数y, 即resolvePromise(y);那么执行[[Resolve]](promise1, y); 这里是一个递归。 - 如果在调用
rejectPromise时提供了一个参数r, 即rejectPromise(y); 那么将promise1的reason设为r;并且promise1的状态也会切换到rejected。 - 如果
resolvePromise和rejectPromise都被数次调用,那么优先第一个调用,其余调用都将被忽略。 - 如果
then方法内部抛出一个异常e:- 如果
resolvePromise和rejectPromise已经被调用,那么忽略它。 - 否则,将
promise1的reason设为e;并且将并且promise1的状态切换到rejected
- 如果
PD:
x不是Promise实例x不是对象或函数。
- 将
promise1的状态设为fulfilled,并且将promise1的value设为x。
then返回值解析:
例如:
promise2 = promise1.then(onFulfilled, onRejected);
onFulfilled或onRejected返回一个值x。 执行[[Resolve]](promise2, x)onFulfilled或onRejected如果抛出一个异常e。那么promise2状态切换为rejected,其reason设为e- 当
onFulfilled或onRejected都不是函数时,则:- 如果
promise1处于fulfilled状态,那么promise2也必须切换到fulfilled状态,并且将promise2的value设为promise1的value - 如果
promise1处于rejected状态,那么promise2也必须切换到rejected状态,并且将promise2的reason设为promise1的reason
- 如果
okay.
验证
情形1: x 为 Promise实例
var pr0 = Promise.reject('wrong,ha,ha,..')
var pr1 = pr0.then(undefined, r=>{
return Promise.reject('Madadesu')
})
console.log(pr1); // pending
setTimeout(console.log, 0,pr1)
pr1.catch(()=>{});
输出:

情形2:x 是非thenable 的对象
var pr0 = Promise.resolve({name:'Tadashi',age:30})
console.log(pr0);
输出:

情形3:x 是一个 thenable对象
var x = {
name:'tadane',
age:30,
then(res,rej){
res(this.name+' '+this.age)
console.log(11111); // 输出
rej('tareru out!!')
console.log(22222); // 输出
throw new Error('....')
console.log(3333); // 未执行
}
}
var pr0 = Promise.resolve(x)
setTimeout(console.log, 0, pr0)
输出:

情形4:抛出异常
var x = {
name:'tadane',
age:30,
then(res,rej){
throw new Error('bad js')
}
}
var pr0 = Promise.resolve(x)
setTimeout(console.log, 0, pr0)
pr0.catch((e)=>{console.log(e.message); })
仍然是 PC。其次,即使是在其他情形中,一旦抛出异常,promise也总是被设为rejected。

情形5: x为原始值或undefined
var p0 = Promise.reject('bad js')
var p1 = p0.then(null, r=>{
return 1111
})
setTimeout(console.log,0,p1)
输出

常见情形就这么几个例子吧。 如果有其他的,可以自行验证
画个图
哎,我也搞个图。

最后
原本想带上catch、 finally什么的;但发现算了。概念又都堆到一起去了。而且这些本来就没什么难度,所以就跳过吧。
下一篇,想搞一个async function 和 async generator,这样衔接应该没什么问题吧……,入门时候都应该学过……吧。。javascript里面我感觉最有意思的部分就是生成器这里了。这里真的向其他地方借鉴了不少(虽然只是语法糖,并没有在底层提供支持),搞得我险些以为学的不是Javascript。
本文有点标题党,虽然是参考Promise/A+规范了,但也大改特改了一番,难免有失误,还请各位多多指正……