这是一道有着成熟的业界规范的 coding 题,完成这道题的前置知识就是要了解什么是 Promises/A+。
这道题的难点就在于它是有规范的,任何一个不满足所有规范条件的解答都是错误的。同时,成熟的规范也配套了成熟的测试用例,官方提供了 872 个测试用例针对规范中的所有条件一一进行检测,哪怕只有一条失败,那也是错误的解答。
而这道题的答题关键也恰恰是因为它是有规范的,只要我们对于规范了然于胸,那么编写代码自然也是水到渠成。因为官方规范提供了一个符合 Promises/A+ 规范的 Promise 应该具有的全部条件,并且在 Requirements 一节中结构清晰、逻辑充分的表述了出来,我们只需将规范中的文字转变为代码,就能够实现一个 Promises/A+ 规范的 Promise。
因为规范条例较多,我们拆解成三块来理解记忆,分别是:基础框架、then 方法和 Promise 处理程序。
每一块由两部分构成:
流程图:展示了代码逻辑的关键步骤,也是优先需要理解记忆的点。
实现代码:展示了代码逻辑的具体细节,是对关键步骤的完善补全。
其中,涉及到规范条例的点会注明规范序号。
再次强调,本题的答题关键是熟悉规范!磨刀不误砍柴工,务必先熟悉!熟悉!熟悉!

1function Promise(executor) {
2 // 2.1. Promise 的状态
3 // Promise 必须处于以下三种状态之一:pending,fulfilled 或者 rejected。
4 this.state = "pending";
5 // 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。
6 this.onFulfilledCallback = [];
7 // 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。
8 this.onRejectedCallback = [];
9
10 const self = this;
11
12 function resolve(value) {
13 setTimeout(function () {
14 // 2.1.1. 当 Promise 处于 pending 状态时:
15 // 2.1.1.1. 可以转换到 fulfilled 或 rejected 状态。
16 // 2.1.2. 当 Promise 处于 fulfilled 状态时:
17 // 2.1.2.1. 不得过渡到任何其他状态。
18 // 2.1.2.2. 必须有一个不能改变的值。
19 if (self.state === "pending") {
20 self.state = "fulfilled";
21 self.data = value;
22 // 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。
23 for (let i = 0; i < self.onFulfilledCallback.length; i++) {
24 self.onFulfilledCallback[i](value);
25 }
26 }
27 });
28 }
29
30 function reject(reason) {
31 setTimeout(function () {
32 // 2.1.1. 当 Promise 处于 pending 状态时:
33 // 2.1.1.1. 可以转换到 fulfilled 或 rejected 状态。
34 // 2.1.3. 当 Promise 处于 rejected 状态时:
35 // 2.1.2.1. 不得过渡到任何其他状态。
36 // 2.1.2.2. 必须有一个不能改变的值。
37 if (self.state === "pending") {
38 self.state = "rejected";
39 self.data = reason;
40 // 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。
41 for (let i = 0; i < self.onRejectedCallback.length; i++) {
42 self.onRejectedCallback[i](reason);
43 }
44 }
45 });
46 }
47
48 // 补充说明:用户传入的函数可能也会执行异常,所以这里用 try...catch 包裹
49 try {
50 executor(resolve, reject);
51 } catch (reason) {
52 reject(reason);
53 }
54}
1// 2.2. then 方法
2// 一个 promise 必须提供一个 then 方法来访问其当前值或最终值或 rejected 的原因。
3// 一个 promise 的 then 方法接受两个参数:
4// promise.then(onFulfilled, onRejected)
5Promise.prototype.then = function (onFulfilled, onRejected) {
6 const self = this;
7
8 let promise2;
9 // 2.2.7. then 必须返回一个 promise
10 return (promise2 = new Promise(function (resolve, reject) {
11 // 2.2.2. 如果 onFulfilled 是一个函数:
12 // 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。
13 // 2.2.2.2. 它一定不能在 promise 的状态变为 fulfilled 前被调用。
14 // 2.2.2.3. 它最多只能被调用一次。
15 if (self.state === "fulfilled") {
16 // 2.2.4. onFulfilled 或 onRejected 在执行上下文堆栈仅包含平台代码之前不得调用。
17 // 3.1. 这可以通过“宏任务”机制(例如 setTimeout 或 setImmediate)或“微任务”机制(例如 MutationObserver 或 process.nextTick)来实现。
18 setTimeout(function () {
19 // 2.2.1. onFulfilled 和 onRejected 都是可选参数:
20 // 2.2.1.1. 如果 onFulfilled 不是一个函数,它必须被忽略。
21 if (typeof onFulfilled === "function") {
22 try {
23 // 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。
24 // 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
25 const x = onFulfilled(self.data);
26 // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
27 promiseResolutionProcedure(promise2, x, resolve, reject);
28 } catch (e) {
29 // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
30 reject(e);
31 }
32 } else {
33 // 2.2.7.3. 如果 onFulfilled 不是一个函数且 promise1 为 fulfilled 状态,promise2 必须用和 promise1 一样的值来变为 fulfilled 状态。
34 resolve(self.data);
35 }
36 });
37 }
38 // 2.2.3. 如果 onRejected 是一个函数,
39 // 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。
40 // 2.2.3.2. 它一定不能在 promise 的状态变为 rejected 前被调用。
41 // 2.2.3.3. 它最多只能被调用一次。
42 else if (self.state === "rejected") {
43 // 2.2.4. onFulfilled 或 onRejected 在执行上下文堆栈仅包含平台代码之前不得调用。
44 // 3.1. 这可以通过“宏任务”机制(例如 setTimeout 或 setImmediate)或“微任务”机制(例如 MutationObserver 或 process.nextTick)来实现。
45 setTimeout(function () {
46 // 2.2.1. onFulfilled 和 onRejected 都是可选参数:
47 // 2.2.1.2. 如果 onRejected 不是一个函数,它必须被忽略。
48 if (typeof onRejected === "function") {
49 try {
50 // 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。
51 // 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
52 const x = onRejected(self.data);
53 // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
54 promiseResolutionProcedure(promise2, x, resolve, reject);
55 } catch (e) {
56 // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
57 reject(e);
58 }
59 }
60 // 2.2.7.4. 如果 onRejected 不是一个函数且 promise1 为 rejected 状态,promise2 必须用和 promise1 一样的 reason 来变为 rejected 状态。
61 else {
62 reject(self.data);
63 }
64 });
65 } else if (self.state === "pending") {
66 // 2.2.6. then 可能会被同一个 promise 多次调用。
67
68 // 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。
69 self.onFulfilledCallback.push(function (promise1Value) {
70 if (typeof onFulfilled === "function") {
71 try {
72 // 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。
73 // 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
74 const x = onFulfilled(self.data);
75 // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
76 promiseResolutionProcedure(promise2, x, resolve, reject);
77 } catch (e) {
78 // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
79 reject(e);
80 }
81 }
82 // 2.2.7.3. 如果 onFulfilled 不是一个函数且 promise1 为 fulfilled 状态,promise2 必须用和 promise1 一样的值来变为 fulfilled 状态。
83 else {
84 resolve(promise1Value);
85 }
86 });
87 // 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。
88 self.onRejectedCallback.push(function (promise1Reason) {
89 if (typeof onRejected === "function") {
90 try {
91 // 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。
92 // 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
93 const x = onRejected(self.data);
94 // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
95 promiseResolutionProcedure(promise2, x, resolve, reject);
96 } catch (e) {
97 // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
98 reject(e);
99 }
100 }
101 // 2.2.7.4. 如果 onRejected 不是一个函数且 promise1 为 rejected 状态,promise2 必须用和 promise1 一样的 reason 来变为 rejected 状态。
102 else {
103 reject(promise1Reason);
104 }
105 });
106 }
107 }));
108};
1// 2.3. Promise 处理程序
2// Promise 处理程序是一个将 promise 和 value 作为输入的抽象操作,我们将其表示为 [[Resolve]](promise, x)。
3// 补充说明:这里我们将 resolve 和 reject 也传入进来,因为后续要根据不同的逻辑对 promise 执行 fulfill 或 reject 操作。
4function promiseResolutionProcedure(promise2, x, resolve, reject) {
5 // 2.3.1. 如果 promise 和 x 引用的是同一个对象,promise 将以一个 TypeError 作为 reason 来进行 reject。
6 if (promise2 === x) {
7 return reject(new TypeError("Chaining cycle detected for promise"));
8 }
9
10 // 2.3.2. 如果 x 是一个 promise,根据它的状态:
11 if (x instanceof Promise) {
12 // 2.3.2.1. 如果 x 的状态为 pending,promise 必须保持 pending 状态直到 x 的状态变为 fulfilled 或 rejected。
13 if (x.state === "pending") {
14 x.then(function (value) {
15 promiseResolutionProcedure(promise2, value, resolve, reject);
16 }, reject);
17 }
18 // 2.3.2.2. 如果 x 的状态为 fulfilled,那么 promise 也用同样的值来执行 fulfill 操作。
19 else if (x.state === "fulfilled") {
20 resolve(x.data);
21 }
22 // 2.3.2.3. 如果 x 的状态为 rejected,那么 promise 也用同样的 reason 来执行 reject 操作。
23 else if (x.state === "rejected") {
24 reject(x.data);
25 }
26 return;
27 }
28
29 // 2.3.3. 除此之外,如果 x 是一个对象或者函数,
30 if (x && (typeof x === "object" || typeof x === "function")) {
31 // 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者多次调用同样的参数,则第一次调用优先,任何之后的调用都将被忽略。
32 let isCalled = false;
33
34 try {
35 // 2.3.3.1. 声明一个 then 变量来保存 then
36 let then = x.then;
37 // 2.3.3.3. 如果 then 是一个函数,将 x 作为 this 来调用它,第一个参数为 resolvePromise,第二个参数为 rejectPromise,其中:
38 if (typeof then === "function") {
39 then.call(
40 x,
41 // 2.3.3.3.1. 假设 resolvePromise 使用一个名为 y 的值来调用,运行 promise 处理程序 [[Resolve]](promise, y)。
42 function resolvePromise(y) {
43 // 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者多次调用同样的参数,则第一次调用优先,任何之后的调用都将被忽略。
44 if (isCalled) return;
45 isCalled = true;
46 return promiseResolutionProcedure(promise2, y, resolve, reject);
47 },
48 // 2.3.3.3.2. 假设 rejectPromise 使用一个名为 r 的 reason 来调用,则用 r 作为 reason 对 promise 执行 reject 操作。
49 function rejectPromise(r) {
50 // 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者多次调用同样的参数,则第一次调用优先,任何之后的调用都将被忽略。
51 if (isCalled) return;
52 isCalled = true;
53 return reject(r);
54 }
55 );
56 }
57 // 2.3.3.4. 如果 then 不是一个函数,使用 x 作为值对 promise 执行 fulfill 操作。
58 else {
59 resolve(x);
60 }
61 } catch (e) {
62 // 2.3.3.2. 如果检索 x.then 的结果抛出异常 e,使用 e 作为 reason 对 promise 执行 reject 操作。
63 // 2.3.3.3.4. 如果调用 then 时抛出一个异常 e,
64 // 2.3.3.3.4.1. 如果 resolvePromise 或 rejectPromise 已经被调用过了,则忽略异常。
65 if (isCalled) return;
66 isCalled = true;
67 // 2.3.3.3.4.2. 否则,使用 e 作为 reason 对 promise 执行 reject 操作。
68 reject(e);
69 }
70 }
71 // 2.3.4. 如果 x 不是一个对象或者函数,使用 x 作为值对 promise 执行 fulfill 操作。
72 else {
73 resolve(x);
74 }
75}1function Promise(executor) {
2 this.state = "pending";
3 this.onFulfilledCallback = [];
4 this.onRejectedCallback = [];
5
6 const self = this;
7
8 function resolve(value) {
9 setTimeout(function () {
10 if (self.state === "pending") {
11 self.state = "fulfilled";
12 self.data = value;
13 for (let i = 0; i < self.onFulfilledCallback.length; i++) {
14 self.onFulfilledCallback[i](value);
15 }
16 }
17 });
18 }
19
20 function reject(reason) {
21 setTimeout(function () {
22 if (self.state === "pending") {
23 self.state = "rejected";
24 self.data = reason;
25 for (let i = 0; i < self.onRejectedCallback.length; i++) {
26 self.onRejectedCallback[i](reason);
27 }
28 }
29 });
30 }
31
32 try {
33 executor(resolve, reject);
34 } catch (reason) {
35 reject(reason);
36 }
37}
38
39Promise.prototype.then = function (onFulfilled, onRejected) {
40 const self = this;
41
42 let promise2;
43
44 return (promise2 = new Promise(function (resolve, reject) {
45 if (self.state === "fulfilled") {
46 setTimeout(function () {
47 if (typeof onFulfilled === "function") {
48 try {
49 const x = onFulfilled(self.data);
50
51 promiseResolutionProcedure(promise2, x, resolve, reject);
52 } catch (e) {
53 reject(e);
54 }
55 } else {
56 resolve(self.data);
57 }
58 });
59 } else if (self.state === "rejected") {
60 setTimeout(function () {
61 if (typeof onRejected === "function") {
62 try {
63 const x = onRejected(self.data);
64
65 promiseResolutionProcedure(promise2, x, resolve, reject);
66 } catch (e) {
67 reject(e);
68 }
69 } else {
70 reject(self.data);
71 }
72 });
73 } else if (self.state === "pending") {
74 self.onFulfilledCallback.push(function (promise1Value) {
75 if (typeof onFulfilled === "function") {
76 try {
77 const x = onFulfilled(self.data);
78
79 promiseResolutionProcedure(promise2, x, resolve, reject);
80 } catch (e) {
81 reject(e);
82 }
83 } else {
84 resolve(promise1Value);
85 }
86 });
87
88 self.onRejectedCallback.push(function (promise1Reason) {
89 if (typeof onRejected === "function") {
90 try {
91 const x = onRejected(self.data);
92
93 promiseResolutionProcedure(promise2, x, resolve, reject);
94 } catch (e) {
95 reject(e);
96 }
97 } else {
98 reject(promise1Reason);
99 }
100 });
101 }
102 }));
103};
104
105function promiseResolutionProcedure(promise2, x, resolve, reject) {
106 if (promise2 === x) {
107 return reject(new TypeError("Chaining cycle detected for promise"));
108 }
109
110 if (x instanceof Promise) {
111 if (x.state === "pending") {
112 x.then(function (value) {
113 promiseResolutionProcedure(promise2, value, resolve, reject);
114 }, reject);
115 } else if (x.state === "fulfilled") {
116 resolve(x.data);
117 } else if (x.state === "rejected") {
118 reject(x.data);
119 }
120 return;
121 }
122
123 if (x && (typeof x === "object" || typeof x === "function")) {
124 let isCalled = false;
125
126 try {
127 let then = x.then;
128
129 if (typeof then === "function") {
130 then.call(
131 x,
132 function resolvePromise(y) {
133 if (isCalled) return;
134 isCalled = true;
135 return promiseResolutionProcedure(promise2, y, resolve, reject);
136 },
137 function rejectPromise(r) {
138 if (isCalled) return;
139 isCalled = true;
140 return reject(r);
141 }
142 );
143 } else {
144 resolve(x);
145 }
146 } catch (e) {
147 if (isCalled) return;
148 isCalled = true;
149 reject(e);
150 }
151 } else {
152 resolve(x);
153 }
154}
155
156module.exports = Promise;开头我们就说过,Promises/A+ 规范配套了成熟的测试用例,我们必须全部通过才算代码编写正确。下面我们就用 872 个官方测试用例来测试一下我们的完整代码是否符合 Promises/A+ 规范。
1// test.js
2
3// 导入我们写好的 promise
4const Promise = require("./promise.js");
5
6// 根据官方文档暴露一个 deferred 方法,返回一个包含 promise、resolve、reject 的对象
7Promise.deferred = function () {
8 const obj = {};
9
10 obj.promise = new Promise(function (resolve, reject) {
11 obj.resolve = resolve;
12 obj.reject = reject;
13 });
14
15 return obj;
16};
17
18module.exports = Promise;1$ npx promises-aplus-tests test.js
完美通过!