前言

你可能经常使用 ​​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​​ 了。

参考文献