感觉有帮助的小伙伴请点赞👍鼓励一下 ~
前言
你可能经常使用 Promise?但你知道你使用的 Promise 是怎么来的么?你知道 Promise 遵循什么规范吗?
Promise 的规范有很多,如Promise/A,Promise/B,Promise/D 以及 Promise/A 的升级版 Promise/A+。ES6 中采用了 Promise/A+ 规范。
所以我们今天就讲一讲 Promise/A+ 规范。
任何符合 Promise 规范的对象或函数都可以成为 Promise, 我们使用的 Promise 也不过是符合 Promise/A+ 规范的其中一种形式,你也可以自己封装一个符合规范的函数,那么你写的函数也可以叫 Promise。
术语
- Promise(实例): 是具有
then方法的对象或函数,其行为符合此规范。 - thenable(具有then方法): 是一个定义
then方法的对象或函数。 - value(值): 是任意合法的
Javascript值,(包括undefined,thenable,promise)。 - exception(异常): 是使用
throw语句抛出的值。 - reason(原因): 是表示
promise为什么被rejected的原因,也就是要throw一个error。
要求
Promise的状态
一个 Promise 的当前状态必须为以下三种状态中的一种:等待态(Pending)、已完成(Fulfilled)和已拒绝(Rejected)。
- 处于等待态时,
Promise需满足以下条件:可以变成 已完成 或 已拒绝 。 - 处于已完成时,
Promise需满足以下条件:- 不能迁移至其它任何状态
- 必须拥有一个 不可变 的值
- 处于已拒绝时,
Promise需满足以下条件:- 不能迁移至其它任何状态
- 必须拥有一个 不可变 的原因
必须有一个then方法
一个 Promise 必须提供一个 then 方法以访问其当前值和原因。 也就是说当 Promise 的状态由 等待态 变为 已完成 或 已拒绝 时,得有一个地方注册回调函数。
-
Promise的then方法接收 两个可选参数 ,Promise.then(onFulfilled, onRejected),两个参数必须是 函数,如果不是函数,则需要 忽略 它们。 -
onFulfilled
- 当
Promise执行结束后,onFulfilled必须被调用,其第一个参数为Promise的值。 - 在
Promise执行结束前,onFulfilled不可被调用。 onFulfilled的调用次数不可超过一次。
- 当
-
onRejected
- 当
Promise被拒绝执行后,onRejected必须被调用,其第一个参数为Promise的原因。 - 在
Promise被拒绝执行前,onRejected不可被调用。 onRejected的调用次数不可超过一次。
- 当
-
在执行上下文堆栈仅包含平台代码之前,不得调用
onFulfilled和onRejected,这个跟JavaScript中的Event Loop相关,在当前的循环中,同步代码执行完之前不可以执行onFulfilled和onRejected这两个函数。 -
onFulfilled和onRejected必须被作为普通函数调⽤(即⾮实例化调⽤(new Function),这样函数内部this⾮严格模式下指向window)。 -
then⽅法可以被同⼀个Promise调⽤多次:- 当
Promise成功执⾏时,所有onFulfilled需按照其注册顺序依次回调。 - 当
Promise被拒绝执⾏时,所有的onRejected需按照其注册顺序依次回调。
- 当
-
then⽅法必须返回⼀个Promise对象,promise2 = promise1.then(onFulfilled, onRejected)-
只要
onFulfilled或onRejected返回一个值x,promise2都会进⼊onFulfilled状态。 -
如果
onFulfilled或onRejected抛出一个异常e, 则promise2必须被拒绝执行并把e当作原因返回。 -
如果
onFulfilled不是一个函数,并且promise1已经完成,promise2必须成功执行并返回相同的值。 -
如果
onRejected不是一个函数, 并且promise1已经被拒绝,promise2必须执行拒绝回调并返回相同的拒因。 -
下面来看一个例子。
-
const promise1 = new Promise((resolve, reject) => reject())
// promise1 手动调用 reject 置为已拒绝状态 , 此时会执行第二个参数的回调函数. 返回 123.
promise1
// 根据只要 `onFulfilled` 或 `onRejected` 返回一个值 `x` ,`promise2` 都会进⼊ `onFulfilled` 状态 , 值为 123.
.then(null, () => {
return 123
})
//- 根据如果 `onFulfilled` 不是一个函数,并且 `promise1` 已经完成, `promise2` 必须成功执行并返回相同的值。 值依然为 123.
.then(null, null)
// 同上 值依然为 123.
.then(null, null)
// 根据如果 `onFulfilled` 不是一个函数,并且 `promise1` 已经完成, `promise2` 必须成功执行并返回相同的值。 打印 promise2 已完成,123
.then(
(res) => { console.log('promise2 已完成', res) }, //=> promise2 已完成 123
(res) => { console.log('promise2 已拒绝', res) }
)
Promise的解决过程
Promise 解决程序是一个抽象操作,我们将其表示为[[Resolve]](promise, x),它以一个 promise 和一个值作为输入。 (这句话的意思就是把 promise 的状态置为 resolve ,同时传入 x 作为值)。
promise.then((x) => {
console.log('会执行这个函数,同时传入x变量的值', x);
});
如果 x 有 then 方法且看上去像一个 Promise , 解决程序就会尝试使 Promise 接受 x 的状态,否则就用 x 的值来执行 Promise 。
如果 Promise 和 x 都指向同一个对象
以 TypeError 为拒因拒绝执行 Promise 。这个没有实现,有大佬知道怎么回事的话,请评论区指教一下。
如果 x 为 Promise ,则使 Promise 接受 x 的状态 :
如果 x 处于等待态, Promise 需保持为等待态直至 x 被执行或拒绝。
const promise1 = new Promise((resolve, reject) => {
setInterval(() => {
resolve('已完成')
}, 3000)
})
const promise2 = new Promise((resolve, reject) => resolve(promise1))
promise2.then(
(val) => {
console.log(val); // 3000ms 后输出 已完成
},
(val) => {
console.log(val);
}
)
如果 x 处于已完成态,用相同的值执行 Promise。
const promise1 = new Promise((resolve, reject) => resolve('已完成'))
const promise2 = new Promise((resolve, reject) => resolve(promise1))
promise2.then(
(val) => {
console.log(val); // 输出 已完成
},
(val) => {
console.log(val);
}
)
如果 x 处于已拒绝态,用相同的据因拒绝 Promise。
const promise1 = new Promise((resolve, reject) => reject('已拒绝'))
const promise2 = new Promise((resolve, reject) => resolve(promise1))
promise2.then(
(val) => {
console.log(val);
},
(val) => {
console.log(val);// 输出 已拒绝
}
)
如果 x 为对象或者函数
1. 首先尝试执行 x.then。
const promise = new Promise((resolve, reject) => resolve({
then: () => console.log('hello,promise') //=> hello,promise
}))
promise.then((x) => {
// 首先尝试执行 `x.then` ,输出 hello,promise 。
})
2. 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 Promise 。
const promise = new Promise((resolve, reject) => resolve({
get then() {
throw Error("我要拒绝") // error
}
}))
promise.then(
(val) => {
console.log(val);
},
(val) => {
console.log(val); //=> error 我要拒绝
}
)
3.如果 then 不为函数,以 x 为参数将 Promise 变为已完成状态。
const promise = new Promise((resolve, reject) => resolve({
name: 'warbler'
}))
promise.then(
(val) => {
console.log(val.name); //=> warbler
},
(val) => {
console.log(val);
}
)
4. 如果 x.then 是函数,
将 x 作为函数的作用域 this 调用。传递两个回调函数作为参数,第一个参数叫做 resolvePromise,第二个参数叫做 rejectPromise。
4.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
const promise = new Promise((resolve, reject) => resolve({
then: (resolvePromise, rejectPromise) => {
resolvePromise('已完成')
}
}))
promise.then(
(val) => {
console.log(val); //=> 已完成
},
(val) => {
console.log(val);
}
)
4.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
const promise = new Promise((resolve, reject) => resolve({
then: (resolvePromise, rejectPromise) => {
rejectPromise('已拒绝')
}
}))
promise.then(
(val) => {
console.log(val);
},
(val) => {
console.log(val); //=> 已拒绝
}
)
4.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
const promise1 = new Promise((resolve, reject) => resolve({
then: (resolvePromise, rejectPromise) => {
resolvePromise('已完成') // 生效
resolvePromise('已完成') // 忽略
rejectPromise('已拒绝') // 忽略
}
}))
promise1.then(
(val) => {
console.log(val); //=> 已完成
},
(val) => {
console.log(val);
}
)
4.4 如果调用 then 方法抛出了异常 e:
如果 resolvePromise 或 rejectPromise 已经被调用,则忽略。
const promise = new Promise((resolve, reject) => resolve({
then: (resolvePromise, rejectPromise) => {
resolvePromise('已完成')
throw new Error("我要拒绝") // 忽略
}
}))
promise.then(
(val) => {
console.log(val); //=> 已完成
},
(val) => {
console.log(val);
}
)
否则以 e 为据因拒绝 promise。
const promise = new Promise((resolve, reject) => resolve({
then: (resolvePromise, rejectPromise) => {
throw new Error("我要拒绝")
}
}))
promise.then(
(val) => {
console.log(val);
},
(val) => {
console.log(val); //=> error 我要拒绝
}
)
如果 x 不为对象或者函数,以 x 为参数将 Promise 变为已完成状态。
const promise = new Promise((resolve, reject) => resolve('warbler'))
promise.then(
(val) => {
console.log(val); //=> warbler
},
(val) => {
console.log(val);
}
)
后语
到这里 Promise/A+ 规范就解析完了,后面的任务就是手写 Promise 了。