Promises/A+ 规范解读
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。如何判断一个对象是否是Promise对象,Promise提供了一个规范,凡是符合promise规范的函数和对象都可以称作为promise,其官方地址:promisesaplus.com/
本文是对官网提供的规范自己部分理解,并不是很全面
1. 术语
- Promise: promise 是具有 then 行为符合本规范的方法的对象或函数。
- thenable:一个定义了then 方法的对象和函数
- value:包括任何 JavaScript 中合法的值(包括undefined,thenable 和 promise)
- exception:使用 throw 抛出的一个值
- reason:表示 promise 拒绝原因的一个值
由此可见 promise 的术语很少,也不难理解
2. 要求
2.1 promise 的状态
一个promise 必须处于以下三个状态之一:等待中(Pending)、已完成(Fulfilled)、已拒绝(Rejected);
- 处于等待中(Pending):可以转换为已完成(Fulfilled)或已拒绝(Rejected)状态
- 处于已完成(Fulfilled):不能够转换为别的状态,必须有一个不可变的值
- 处于已拒绝(Rejected):不能够转换为别的状态,必须有一个不可变的值来表示拒绝的原因
2.2 then 方法
一个promise 必须提供一个then方法去访问当前值和原因,在promise的then方法中接受两个参数
promise.then(onFulfilled, onRejected)
- 两个参数都不是必须的并且都是函数,如果不是函数,需要忽略他们
- 如果onFulfilled是一个函数
- 他必须在promise完成之后调用,他的第一个参数是promise返回的结果
- 不能在promise执行前调用
- 不能够被调用多次,只能调用一次
- 如果onRejected是一个函数
- 他必须在promise被拒绝完成之后调用,他的第一个参数是promise拒绝返回的原因
- 不能在promise被拒绝执行前调用
- 不能够被调用多次,只能调用一次。
- onFulfilled和onRejected 不能够在执行上下文堆栈中仅包含平台代码之前调用
提示:先看下面的代码,在JS事件循环中,先观察任务队列中有没有微任务,如果有执行所有的微任务,如果没有按照堆栈顺序执行任务列表中的宏任务。(知识点: js 中的宏任务和微任务)
关于这一条规范 更具体的可以参考官网 3.1
console.log('1'); # 执行上下文平台代码
setTimeout(function() {
console.log('2');
}, 0); # 执行上下文中的宏任务
Promise.resolve().then(function() {
console.log('3');
}); # 执行上下文中的微任务
console.log('4'); # 执行上下文平台代码
打印顺序:1 4 3 2
- onFulfilled 和 onRejected 必须作为一个函数调用(里面没有this值)
- then 方法可以被同一个promise调用多次
- 如果promise状态是Fulfilled时,所有的 onFulfilled 方法按照注册的顺序执行
- 如果promise状态是Rejected时,所有的 onRejected 方法按照注册的顺序执行
- then 方法必须返回一个promise 对象
promise2 = promise1.then(onFulfilled, onRejected);
- 只要 onFulfilled 或 onRejected 返回一个一个值 x, promise2 都会进入 onFulfilled
- 如果 onFulfilled 或 onRejected 抛出一个异常e,则 promise2 必须是rejected状态,并返回拒绝原因 e
- 如果 onFulfilled 不是一个函数,并且 promise1 的状态是已完成,则promise2 必须是已完成状态并且返回相同的值
- 如果 onRejected 不是一个函数,并且 promise1 的状态是已拒绝,则promise2 必须是已拒绝状态并且返回相同拒绝的原因
我们写一段代码分别看一下上面的规范
# 1.
const promise1 = Promise.reslve();
promise1.then(()=>{return "Lius"}).then(res=>{console.log(res)}) # Lius
const promise1 = Promise.reject();
promise1
.then(null, () => {
return "Lius";
})
.then(
(res) => {
console.log("Fulfilled", res); // # Lius
},
(err) => {
console.log("Rejected", err);
}
);
# 2 不演示了
# 3
const promise1 = Promise.reslve("Lius");
promise1
.then(null, (err) => {
console.log("promise1 reject", err);
})
.then((res) => {
console.log("promise2 fulfilled",res); // Lius
});
# 4
const promise1 = Promise.reject("Lius");
promise1
.then((res) => {
console.log("promise1 fulfilled", res);
},null)
.then((res) => {
console.log("promise2 fulfilled", res);
},(err) => {
console.log("promise2 reject", err); // Lius
});
2.3 promise 的解析过程
Promise 的解析过程是一个抽象的操作,输入一个promise和值x,记作:[[Resolve]]``(promise, x) 如果 x 有 then 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;否则其用 x 的值来执行 promise 。
这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。
表示 [[Resolve]](promise, x) 的过程如下
- 如果 promise 和 x 指向同一个对象,则会抛出typeError拒绝promise
- 如果 x 是一个 promise 对象
- 如果 x 处于等待状态,promise需要保持等待状态直到状态被完成或拒绝
- 如果 x 处于完成状态,使用相同的值完成promise
- 如果 x 处于拒绝状态,使用相同的值拒绝promise
- 如果 x 是一个对象或者函数
- 先取 x.then 的值
- 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝
promise - 如果 then 是一个函数,将 x 作为函数的this 作用域,传递两个参数 第一个参数是resolvePromise 第二个参数是rejectPromise
- 如果resolvePromise以值y 被调用,则运行 [[Resolve]](promise, y)
- 如果rejectPromise以值y 被调用,则运行 [[Reject]](promise, y)
- 如果两者都被调用并且被调用多次,则优先采用首次调用结果,并忽略其他调用
// 以上三点使用代码表示一下
const promise = Promise.resolve({
then: (resolveFun, rejectFun) => {
resolveFun("XS") // 调用 rejectFun 会执行catch
}
})
promise.then(res => {
console.log("状态完成", res) // 状态完成 XS
}).catch(err => {
console.log("状态拒绝", err)
})
- 如果调用 then 方法时抛出了异常 e
- 如果resolvePromise和rejectPromise已经被调用,则忽略异常
- 否则以e 作为结果拒绝
const promise = Promise.resolve({
then: (resolveFun, rejectFun) => {
resolveFun("XS")
rejectFun("error")
throw new Error("抛出异常")
}
})
promise.then(res => {
console.log("状态完成", res)
}).catch(err => {
console.log("状态拒绝", err)
})
// 如果在抛出异常之前指向resolveFun 或者 rejectFun,会执行到对应的方法回调,否则以抛出的异常拒绝promise
- 如果 then 不为函数,以 x 为参数将 promise 变为已完成状态
- 如果x 不是一个对象或者函数,以 x 为参数将 promise 变为已完成状态
3. 结束语
很艰难的把promise A+ 规范解读一遍,其中参考了网上很多文章,自己理解有90%左右,还是希望能够去看官网英文原版。 特别提示,红色部分我理解了很久还是没能够弄明白,见谅见谅,也希望又能够补充提示的 下一节,通过promise A+ 规范实现一个promise