这是一道有着成熟的业界规范的 coding 题,完成这道题的前置知识就是要了解什么是 Promises/A+。
这道题的难点就在于它是有规范的,任何一个不满足所有规范条件的解答都是错误的。同时,成熟的规范也配套了成熟的测试用例,官方提供了 872 个测试用例针对规范中的所有条件一一进行检测,哪怕只有一条失败,那也是错误的解答。
而这道题的答题关键也恰恰是因为它是有规范的,只要我们对于规范了然于胸,那么编写代码自然也是水到渠成。因为官方规范提供了一个符合 Promises/A+ 规范的 Promise 应该具有的全部条件,并且在 Requirements 一节中结构清晰、逻辑充分的表述了出来,我们只需将规范中的文字转变为代码,就能够实现一个 Promises/A+ 规范的 Promise。

Promise

Promise 翻译过来就是承诺的意思,这个承诺会在未来有一个确切的答复,并且该承诺有三种状态,分别是:

  1. 等待中(pending)
  2. 完成了(resolved)
  3. 拒绝了(rejected)

Promise的一些特点

  1. Promise状态一旦从等待状态变成为其他状态就永远不能更改状态了。
1
2
3
4
5
new Promise((resolve, reject) => {
resolve('success')
// 无效
reject('reject')
})
  1. 当我们在构造 Promise 的时候,构造函数内部的代码是立即执行的:
1
2
3
4
5
6
new Promise((resolve, reject) => {
console.log('new Promise')
resolve('success')
})
console.log('finifsh')
// new Promise -> finifsh
  1. Promise 实现了链式调用,也就是说每次调用 then 之后返回的都是一个 Promise,并且是一个全新的 Promise,原因也是因为状态不可变。如果你在 then 中 使用了 return,那么 return 的值会被 Promise.resolve() 包装
1
2
3
4
5
6
7
8
Promise.resolve(1)
.then(res => {
console.log(res) // => 1
return 2 // 包装成 Promise.resolve(2)
})
.then(res => {
console.log(res) // => 2
})
  1. Promise 也很好地解决了回调地狱的问题,可以把之前的回调地狱例子改写为如下代码:
1
2
3
4
5
6
7
8
ajax(url)
.then(res => {
console.log(res)
return ajax(url1)
}).then(res => {
console.log(res)
return ajax(url2)
}).then(res => console.log(res))

手写Promise

实现一个简易版的Promise

  1. 大体框架
1
2
3
4
5
6
7
8
9
10
11
12
13
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'

function MyPromise(fn) {
const that = this
that.state = PENDING
that.value = null
that.resolvedCallbacks = []
that.rejectedCallbacks = []
// 待完善 resolve 和 reject 函数
// 待完善执行 fn 函数
}
  • 首先我们创建了三个常量用于表示状态,对于经常使用的一些值都应该通过常量来管理,便于开发及后期维护
  • 在函数体内部首先创建了常量 that,因为代码可能会异步执行,用于获取正确的 this 对象
  • 一开始 Promise 的状态应该是 pending
  • value 变量用于保存 resolve 或者 reject 中传入的值
  • resolvedCallbacks 和 rejectedCallbacks 用于保存 then 中的回调,因为当执行完 Promise 时状态可能还是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用
  1. 接下来完善resolve 和 reject 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function resolve(value) {
if (that.state === PENDING) {
that.state = RESOLVED
that.value = value
that.resolvedCallbacks.map(cb => cb(that.value))
}
}

function reject(value) {
if (that.state === PENDING) {
that.state = REJECTED
that.value = value
that.rejectedCallbacks.map(cb => cb(that.value))
}
}

这两个函数代码类似,就一起解析了

  • 首先两个函数都得判断当前状态是否为等待中,因为规范规定只有等待态才可以改变状态
  • 将当前状态更改为对应状态,并且将传入的值赋值给 value
  • 遍历回调数组并执行
  1. 完成以上两个函数以后,我们就该实现如何执行 Promise 中传入的函数了
1
2
3
4
5
try {
fn(resolve, reject)
} catch (e) {
reject(e)
}

实现很简单,执行传入的参数并且将之前两个函数当做参数传进去

接下来实现较为复杂的then函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
MyPromise.prototype.then = function(onFulfilled, onRejected) {
const that = this
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected =
typeof onRejected === 'function'
? onRejected
: r => {
throw r
}
if (that.state === PENDING) {
that.resolvedCallbacks.push(onFulfilled)
that.rejectedCallbacks.push(onRejected)
}
if (that.state === RESOLVED) {
onFulfilled(that.value)
}
if (that.state === REJECTED) {
onRejected(that.value)
}
}
  • 首先判断两个参数是否为函数类型,因为这两个参数是可选参数
  • 当参数不是函数类型时,需要创建一个函数赋值给对应的参数,同时也实现了透传。

以上就是简单版 Promise 实现,接下来一小节是实现完整版 Promise 的解析,相信看完完整版的你,一定会对于 Promise 的理解更上一层楼。

符合 Promise/A+ 规范的 Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
function Promise(executor) {
// 2.1. Promise 的状态
// Promise 必须处于以下三种状态之一:pending,fulfilled 或者 rejected。
this.state = "pending";
// 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。
this.onFulfilledCallback = [];
// 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。
this.onRejectedCallback = [];

const self = this;

function resolve(value) {
setTimeout(function () {
// 2.1.1. 当 Promise 处于 pending 状态时:
// 2.1.1.1. 可以转换到 fulfilled 或 rejected 状态。
// 2.1.2. 当 Promise 处于 fulfilled 状态时:
// 2.1.2.1. 不得过渡到任何其他状态。
// 2.1.2.2. 必须有一个不能改变的值。
if (self.state === "pending") {
self.state = "fulfilled";
self.data = value;
// 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。
for (let i = 0; i < self.onFulfilledCallback.length; i++) {
self.onFulfilledCallback[i](value);
}
}
});
}

function reject(reason) {
setTimeout(function () {
// 2.1.1. 当 Promise 处于 pending 状态时:
// 2.1.1.1. 可以转换到 fulfilled 或 rejected 状态。
// 2.1.3. 当 Promise 处于 rejected 状态时:
// 2.1.2.1. 不得过渡到任何其他状态。
// 2.1.2.2. 必须有一个不能改变的值。
if (self.state === "pending") {
self.state = "rejected";
self.data = reason;
// 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。
for (let i = 0; i < self.onRejectedCallback.length; i++) {
self.onRejectedCallback[i](reason);
}
}
});
}

// 补充说明:用户传入的函数可能也会执行异常,所以这里用 try...catch 包裹
try {
executor(resolve, reject);
} catch (reason) {
reject(reason);
}
}

then方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// 2.2. then 方法
// 一个 promise 必须提供一个 then 方法来访问其当前值或最终值或 rejected 的原因。
// 一个 promise 的 then 方法接受两个参数:
// promise.then(onFulfilled, onRejected)
Promise.prototype.then = function (onFulfilled, onRejected) {
const self = this;

let promise2;
// 2.2.7. then 必须返回一个 promise
return (promise2 = new Promise(function (resolve, reject) {
// 2.2.2. 如果 onFulfilled 是一个函数:
// 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。
// 2.2.2.2. 它一定不能在 promise 的状态变为 fulfilled 前被调用。
// 2.2.2.3. 它最多只能被调用一次。
if (self.state === "fulfilled") {
// 2.2.4. onFulfilled 或 onRejected 在执行上下文堆栈仅包含平台代码之前不得调用。
// 3.1. 这可以通过“宏任务”机制(例如 setTimeout 或 setImmediate)或“微任务”机制(例如 MutationObserver 或 process.nextTick)来实现。
setTimeout(function () {
// 2.2.1. onFulfilled 和 onRejected 都是可选参数:
// 2.2.1.1. 如果 onFulfilled 不是一个函数,它必须被忽略。
if (typeof onFulfilled === "function") {
try {
// 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。
// 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
const x = onFulfilled(self.data);
// 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
promiseResolutionProcedure(promise2, x, resolve, reject);
} catch (e) {
// 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
reject(e);
}
} else {
// 2.2.7.3. 如果 onFulfilled 不是一个函数且 promise1 为 fulfilled 状态,promise2 必须用和 promise1 一样的值来变为 fulfilled 状态。
resolve(self.data);
}
});
}
// 2.2.3. 如果 onRejected 是一个函数,
// 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。
// 2.2.3.2. 它一定不能在 promise 的状态变为 rejected 前被调用。
// 2.2.3.3. 它最多只能被调用一次。
else if (self.state === "rejected") {
// 2.2.4. onFulfilled 或 onRejected 在执行上下文堆栈仅包含平台代码之前不得调用。
// 3.1. 这可以通过“宏任务”机制(例如 setTimeout 或 setImmediate)或“微任务”机制(例如 MutationObserver 或 process.nextTick)来实现。
setTimeout(function () {
// 2.2.1. onFulfilled 和 onRejected 都是可选参数:
// 2.2.1.2. 如果 onRejected 不是一个函数,它必须被忽略。
if (typeof onRejected === "function") {
try {
// 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。
// 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
const x = onRejected(self.data);
// 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
promiseResolutionProcedure(promise2, x, resolve, reject);
} catch (e) {
// 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
reject(e);
}
}
// 2.2.7.4. 如果 onRejected 不是一个函数且 promise1 为 rejected 状态,promise2 必须用和 promise1 一样的 reason 来变为 rejected 状态。
else {
reject(self.data);
}
});
} else if (self.state === "pending") {
// 2.2.6. then 可能会被同一个 promise 多次调用。

// 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。
self.onFulfilledCallback.push(function (promise1Value) {
if (typeof onFulfilled === "function") {
try {
// 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。
// 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
const x = onFulfilled(self.data);
// 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
promiseResolutionProcedure(promise2, x, resolve, reject);
} catch (e) {
// 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
reject(e);
}
}
// 2.2.7.3. 如果 onFulfilled 不是一个函数且 promise1 为 fulfilled 状态,promise2 必须用和 promise1 一样的值来变为 fulfilled 状态。
else {
resolve(promise1Value);
}
});
// 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。
self.onRejectedCallback.push(function (promise1Reason) {
if (typeof onRejected === "function") {
try {
// 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。
// 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
const x = onRejected(self.data);
// 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
promiseResolutionProcedure(promise2, x, resolve, reject);
} catch (e) {
// 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
reject(e);
}
}
// 2.2.7.4. 如果 onRejected 不是一个函数且 promise1 为 rejected 状态,promise2 必须用和 promise1 一样的 reason 来变为 rejected 状态。
else {
reject(promise1Reason);
}
});
}
}));
};