原版的规范参考:promisesaplus.com/
规范的产生背景
该规范首次提出是在2012年左右,它是promise A 规范的升级版
在时间线上,Promise A+是早于ES6的
在那个时候,为了处理异步场景,JS中充斥着各种回调函数:
// 以回调模式处理异步
button.addEventListener("click", function(e){
// 回调函数
})
setTimeout(function(){
// 回调函数
}, 1000)
以前,回调函数也不是唯一的处理异步的方法,有些异步场景也可以把函数保存到对象的属性中,以便调用:
// 保存函数的属性
xhr.onreadstatechange = function() {
}
button.onClick = function() {}
可以看出,过去处理异步的方式是不统一的
在前端处理异步混乱的时代,Promise A+横空出世,制定了一个标准
重点提示:它不是一个技术,而是一套规范
它希望所有的前端开发者都遵循这套规则,使得所有的开发者的异步代码达到统一
后来,ES6在制作Promise 标准时,就参考了该规范
规范翻译
以下内容是翻译的规范内容,稍微抽象,看起来吃力的同学可以直接跳到----解读
一个Promise 对象应该能够表达一个异步操作,异步操作无论是成功还是失败,总会有一个结果,这个结果可以通过它的then方法进行交互,具体的交互方式是,注册一个回调函数到then方法中
该规范详细的描述了then方法的具体规格
Promise
promise 用于表示一个异步任务,它应该是一个带有 then 方法的对象
它有三种状态:pending 挂起,任务进行中、fulfilled 完成、rejected 失败
任何时刻,promise一定处于三种状态之一
ES6中的 Promise 完成状态是 resolved,单词不同,含义相同
-
当 promise 处于 pending 状态时:
它可以在任何合适的时候把状态转变为 fulfilled 或 rejected
-
当 promise 处于 fulfilled 状态时:
它无法再次更改到其他状态
它必须拥有一个值,表示任务完成时的数据,该数据是任何JS数据(甚至可以是undefined),并且这个值一旦确定下来,不可更改
-
当 promise 处于 rejected 状态时:
它无法再次更改到其他状态
它必须拥有一个值,表示任务失败的原因,该数据是任何JS数据(甚至可以是undefined),并且这个值一旦确定下来,不可更改
then 方法
promise 应该提供一个 then方法,通过这个方法,我们可以访问到任务完成时的值 或 任务失败的原因
then 方法可以接收两个参数:
promise.then(onFulfilled, onRejected)
-
onFulfilled 和 onRejected 都是可选参数:
如果 onFulfilled 和 onRejected 不是一个函数,则就必须被忽略
-
如果
onFulfilled是一个函数:它应该在
promise到达fulfilled状态时被调用,调用该函数时,应该把任务完成时的值作为第一个参数传递进去该函数只能被调用一次
-
如果
onRejected是一个函数:它应该在
promise到达rejected状态时被调用,调用该函数时,应该把任务失败的原因作为第一个参数传递进去该函数只能被调用一次
-
onFulfilled和onRejected必须要等到当前执行栈清空后才能被调用,换句话说,它们是异步执行的 -
可以多次对同一个
promise调用then方法,从而注册多个onFulfilled或onRejected当任务完成 或 任务失败时,将按照注册顺序,依次调用注册的onFulfilled或onRejected -
then方法必须再次返回一个promisepromise2 = promise1.then(onFulfilled, onRejected)在执行
onFulfilled或onRejected时,如果执行的过程中报了一个错误,则会导致promise2进入rejected状态,并且传递和状态相关的失败原因,就是抛出错误在执行
onFulfilled或onRejected时,如果执行的过程中没有发生错误,并且返回值是x(x可以是任何数据类型,包括undefined),则会进入 任务完成处理流程[[Resolve]](promise2, x),具体流程在 下一小节 中解释如果
onFulfilled不是一个函数,同时promise1已经fulfilled,promise2也会自动变成fulfilled,完成的相关数据和promise1一致如果
onRejected不是一个函数,同时promise1已经rejected,promise2也会自动变成rejected,失败的原因和promise1一致
任务完成处理流程
这个流程是一个概念上的操作规范,而不是真实的代码
Promise A+期望实现该规范的代码,也要按照这个处理流程去实现
为了便于说明,Promise A+ 把这个操作规范记作[[Resolve]](promise, x),它应该按照以下的流程进行:
-
如果
promise和x是同一个对象,则把promise设置为rejected状态,失败原因是一个TypeError -
如果
x是一个promise:- 如果
x处于pending,promise也必须处于pending,知道x完成或失败 - 如果
x完成,promise也会完成,数据和x完成的数据一致 - 如果
x失败,promise也会失败,数据和x失败原因一致
- 如果
-
如果
x是一个对象或函数:-
读取
x.then,如果读取发生异常,则直接让promise失败,失败原因就是抛出的错误,然后结束处理即可 -
如果
x.then是一个function- 调用
x.then方法:x.then(onFulfilled, onRejected) - 当
onFulfilled执行时,如果得到的数据是y,则执行[[Resolve]](promise, y) - 当
onRejected执行时,如果得到的失败原因是error,则让promise变成失败状态,原因是error
- 调用
-
如果
x.then不是一个function, 把promise变成fulfilled状态,数据为x -
如果调用
x.then的时候发生错误,则把promise变成失败状态,失败原因为抛出的错误
-
-
如果
x不是一个对象或函数,把promise变成fulfilled状态,数据为x
解读
如果对上面内容本身就理解的很透彻,下面内容就可以不必阅读了
1. ES6的Promise 和 Promise A+是一个什么样的关系?
ES6 的 Promise 遵循的是 Promise A+ 的规范
这也就意味着ES6的 Promise 完全可以和其他遵循了 Promise A+ 规范的 Promise 进行相互操作
比如,ES6的 Promise 可以和 JQuery 的Promise进行互操作
$.ajax(...).then(resp=>{
// 这是JQuery的Promise的then函数
return new Promise(resolve=>{
// 这是ES6的Promise
})
})
2. 关于Promise的catch函数
Promise A+ 规范中并没有规定需要提供catch,但ES6的Promise提供了这一函数,它是为了让开发者使用起来更加方便
这并不代表ES6的Promise打破了这一规范,因为绝大部分规范都是只规定了你至少有什么东西,并不关心你多了什么东西
3. Promise必须通过构造函数new Promise得到吗
Promise A+没有这样规定,它可以通过任何途径得到,只不过ES6的Promise是通过构造函数得到的
Promise A+规范,要求Promise必须是一个对象或是一个函数,该对象或函数提供了满足要求的then方法即可
比如,下面的代码得到的都是满足规范的Promise
const promise1 = {
then(onFufilled, onRejected){
// 满足规范的then函数
}
}
const promise2 = function(a, b){return a+b}
promise2.then = function(onFufilled, onRejected){
// 满足规范的then函数
}
4. 关于Promise的状态
Promise A+ 并没有要求Promise必须提供什么属性来得到状态,这些都是Promise的内部信息,你可以对外提供,也可以不提供
ES6的Promise提供了两个属性[[PromiseStatus]]和[[PromiseValue]],分别表示Promise的状态和状态的相关数据(成功的数据或失败的原因),但这两个属性均无法在外部访问
另外,Promise A+ 也没有对状态的名称做出强制要求,在规范文档中,Promise A+ 使用了pending、fufilled、rejected,但这些名称仅仅是为了说明规范而产生的,在具体的实现中,你可以自行规定任何的名称来表示三种状态
ES6的Promise就使用了pending、resolved、rejected来表示三种状态,这并没有打破Promise A+规范