Promise/A+ 规范介绍和实现

Promise/A+ 规范

Promise/A+

promise 是一个带有 then 方法的对象或者函数。

promise 表示异步操作的最终结果。与 promise 交互的主要方式是通过其 then 方法,该方法注册回调以接收 promise 的最终值或 promise 无法实现的原因。

本规范详细说明了 then 方法的行为,提供了一个可互操作的基础,所有符合 Promises/A+ 的 promise 实现都可以依赖于该基础来提供。也就是说,所有符合 Promises/A+ 的 promise 实现,他们之间可以相互操作。

要求
promise 状态

一个 promise 必须是以下三种状态其中的一种状态:

  • pending(等待)
  • fulfilled(完成)
  • rejected(拒绝)

一个 promise 的初始状态是“pending”,在“pending”状态下调用 resolve 方法后,其状态变为“fulfilled”,在“pending”状态下调用 reject 方法后,其状态变为“rejected”。
当一个 promise 的状态不是“pending”时,其状态不能再被改变。

then 方法

一个 promise 必须提供一个 then 方法去访问当前或者最终的 value 或者 reason。
一个 promise 的 then 方法接受两个参数,且其返回一个 promise:

1
Promise.then(onFulfilled?, onRejected?): Promise

(1)当 onFulfilledonRejected 不是函数时,应该被忽略。
(2)当 onFulfilledonRejected 是函数时:

  • 在 promise 变成 fulfilled 时,调用 onFulfilled, 参数是 value
  • 在 promise 变成 rejected 时,调用 onRejected, 参数是 reason

(3)then 返回一个 promise。

1
promise2 = promise1.then(onFulfilled, onRejected);
  • onFulfilled 或 onRejected 是函数且返回 x, 那么 promise2 以 x 触发 fulfilled;
  • 如果 onFulfilled 或者 onRejected 执行时抛出异常 e, promise2 需要被 reject;
  • 如果 onFulfilled 不是一个函数, promise2 以 promise1 的 value 触发 fulfilled;
  • 如果 onRejected 不是一个函数, promise2 以 promise1 的 reason 触发 rejected。

(4)onFulfilled 和 onRejected 应放在微任务队列中被调用。
(5)then 方法可以被同一个 promise 调用多次。


Promise/A+ 实现

采用 TS 实现。

1. 声明类型:
1
2
3
4
5
6
7
8
9
10
11
12
13
type Resolve<T = any> = (value: T) => void;
type Reject = (reason: any) => void;
type OnFulfilled<T = any, K = any> = (value: T) => K | null | undefined;
type OnRejected<K = any> = (reason?: any) => K | null | undefined;
type OnFinally = () => void | null | undefined;
type Executor<T> = (resolve: Resolve<T>, reject: Reject) => void;
type Status = "fulfilled" | "pending" | "rejected";
type Handlers = Array<{
onFulfilled?: OnFulfilled;
onRejected?: OnRejected;
resolve: Resolve;
reject: Reject;
}>
2. 创建类及构造函数

参考 Promise 的构造函数,其接收一个函数 (resolve, reject) => void 为参数,当执行 resolve(value)reject(reason) 后,PromiseState 由 pending 变为 fulfilled 或 rejected,PromiseResult 由 undefined 变为 value 或 reason。
因此,我们用 status 保存状态,用 result 保存结果。resolve 和 reject 方法的作用就是改变它们。

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
class MyPromise<T = unknown> {
private readonly FULFILLED = "fulfilled";
private readonly PENDING = "pending";
private readonly REJECTED = "rejected";

private status: Status = this.PENDING;
private result: any = undefined;

constructor(executor: Executor<T>) {
try {
const resolve = (data: T) => {
this.changeStatus(this.FULFILLED, data);
}
const reject = (reason: any) => {
this.changeStatus(this.REJECTED, reason);
}
executor(resolve, reject);
} catch(error: any) {
this.changeStatus(this.REJECTED, error);
};
}

private changeStatus(state: Status, result: T) {
// 非 pending 状态不能再被改变
if (this.status !== this.PENDING) return;
this.status = state;
this.result = result;
}
}

将状态用常量保存是因为这些状态的名称可能会发生变化,便于维护。
使用 try...catch 是因为 Promise 能捕获到 executor 方法中的语法错误,然后触发 reject。
resolve 和 reject 必须用箭头函数,因为调用 resolve 或 reject 的实际上是 window,不使用箭头函数的话其内部 this 指向 window。

3. 实现 then 方法

在此之前,我们先来考虑何时调用 onFulfilled 和 onRejected。根据规范,当状态变为 fulfilled 时调用 onFulfilled,当状态变为 rejected 时调用 onRejected。那么状态何时改变呢?这里有两种情况(以 onFulfilled 为例):
第一种:

1
2
3
new Promise(resolve => {
resolve(123);
}).then(res => {});

这种情况是在初始化实例时状态就已经改变,那么在调用 then 方法时应立即调用 onFulfilled。
第二种:

1
2
3
4
5
new Promise(resolve => {
setTimeout(() => {
resolve
}, 1000);
}).then(res => {});

这种情况是状态改变发生在调用 then 方法之后,那么应该在 changeStatus 方法中调用 onFulfilled。
此外,then 方法是可以被同一个 promise 多次调用的:

1
2
3
4
5
6
7
8
const p = new Promise(resolve => {
setTimeout(() => {
resolve(123);
}, 1000);
});
p.then(res => {});
p.then(res => {});
p.then(res => {});

then 方法是按照调用的顺序执行的,因此,我们需要用数组模拟队列来保存 onFulfilled 和 onRejected,然后在状态改变时调用。

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
class MyPromise<T = unknown> {
private readonly FULFILLED = "fulfilled";
private readonly PENDING = "pending";
private readonly REJECTED = "rejected";

private status: Status = this.PENDING;
private result: any = undefined;
private handlers: Handlers = [];

constructor(executor: Executor<T>) {
try {
const resolve = (data: T) => {
this.changeStatus(this.FULFILLED, data);
}
const reject = (reason: any) => {
this.changeStatus(this.REJECTED, reason);
}
executor(resolve, reject);
} catch(error: any) {
this.changeStatus(this.REJECTED, error);
};
}

private changeStatus(state: Status, result: T) {
if (this.status !== this.PENDING) return;
this.status = state;
this.result = result;
this.run();
}

private runOne(resolve: Resolve, reject: Reject, callback?: OnFulfilled | OnRejected) {
if (typeof callback === "function") {
try {
const res = callback(this.result);
resolve(res)
} catch(error: any) {
reject(error);
}
} else {
const settled = this.status === this.FULFILLED? resolve : reject;
settled(this.result);
}
}

private run() {
// 状态没有立即发生改变或 then 方法没有传参
if (this.status === this.PENDING || !this.handlers.length) return;

while (this.handlers.length) {
const { onFulfilled, onRejected, resolve, reject } = this.handlers.shift()!;
if (this.status === this.FULFILLED) {
this.runOne(resolve, reject, onFulfilled);
} else {
this.runOne(resolve, reject, onRejected);
}
}
}

then<K>(onFulfilled?: OnFulfilled<T, K>, onRejected?: OnRejected<K>) {
return new MyPromise<K>((resolve, reject) => {
this.handlers.push({
onFulfilled,
onRejected,
resolve,
reject
});
this.run();
});
}
}

如果 onFulfilled 或 onRejected 是函数且返回值是 PromiseLike,其 resolve 的 value 或 reject 的 reason 应该传递到 then 方法返回的 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
private isPromiseLike(value: any) {
if (value !== null && (typeof value === "object" || typeof value === "function")) {
return typeof value.then === "function";
}
return false;
}

private runOne(resolve: Resolve, reject: Reject, callback?: OnFulfilled | OnRejected) {
if (typeof callback === "function") {
try {
const res = callback(this.result);
if (this.isPromiseLike(res)) {
res.then(resolve, reject);
} else {
resolve(res)
}
} catch(error: any) {
reject(error);
}
} else {
const settled = this.status === this.FULFILLED? resolve : reject;
settled(this.result);
}
}

此外,onFulfilled 和 onRejected 方法是异步执行的,这可以通过“宏任务”机制(如 setTimeout 或 setImmediate)或“微任务”机制(如 mutationobserver 或 process.nextick)来实现。

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
// 模拟微任务队列
private microTask(run: () => void) {
// node 环境:nextTick 的回调在本轮微任务执行完毕,下一轮宏任务执行开始之前执行
if (typeof process === "object" && typeof process.nextTick === "function") {
process.nextTick(run);
}

// 浏览器环境:MutationObserver 在监听到 dom 变动之后将回调函数放入微任务队列
else if (typeof MutationObserver === "function") {
const ob = new MutationObserver(run);
const textNode = document.createTextNode("1");
ob.observe(textNode, {
characterData: true
});
textNode.data = "2";
} else {
setTimeout(run, 0);
}
}

private runMicriTask(resolve: Resolve, reject: Reject, callback?: OnFulfilled | OnRejected) {
this.microTask(() => {
if (typeof callback === "function") {
try {
const res = callback(this.result);
if (this.isPromiseLike(res)) {
res.then(resolve, reject);
} else {
resolve(res)
}
} catch(error: any) {
reject(error);
}
} else {
const settled = this.status === this.FULFILLED? resolve : reject;
settled(this.result);
}
});
}
4. 实现 catch 方法

catch 方法就是没有 onFulfilled 的 then 方法。

基于此准则,catch 方法实现如下:

1
2
3
catch<K = never>(onRejected?: OnRejected<K>) {
return this.then<K>(undefined, onRejected);
}
5. 实现 finally 方法

finally 方法的作用就是在 promise 拿到结果之后(无论是完成态还是拒绝态)进行一些处理。

1
2
3
4
5
6
7
8
9
10
11
12
finally(onFinally?: OnFinally) {
return this.then<T>(
() => {
onFinally && onFinally();
return undefined;
},
() => {
onFinally && onFinally();
return undefined;
},
)
}
6. 实现 resolve 和 reject 方法

我们可以发现,在使用 Promise 的时候可以直接调用 Promise.resolve()Promise.reject() 方法。然而在其返回的 Promise 对象中却找不到这两个方法。
基于这个特性,可以用静态方法来实现,因为静态成员可以由类名直接调用,但对象却不能调用。

1
2
3
4
5
6
7
8
9
10
static resolve<K = unknown>(value: K) {
return new MyPromise<K>((resolve => {
resolve(value);
}))
}
static reject<K = never>(reason?: any) {
return new MyPromise<K>((_, reject) => {
reject(reason);
});
}
7. 完整代码
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
type Resolve<T = any> = (value: T) => void;
type Reject = (reason: any) => void;
type OnFulfilled<T = any, K = any> = (value: T) => K | null | undefined;
type OnRejected<K = any> = (reason?: any) => K | null | undefined;
type OnFinally = () => void | null | undefined;
type Executor<T> = (resolve: Resolve<T>, reject: Reject) => void;
type Status = "fulfilled" | "pending" | "rejected";
type Handlers = Array<{
onFulfilled?: OnFulfilled;
onRejected?: OnRejected;
resolve: Resolve;
reject: Reject;
}>


class MyPromise<T = unknown> {
private readonly FULFILLED = "fulfilled";
private readonly PENDING = "pending";
private readonly REJECTED = "rejected";

private status: Status = this.PENDING;
private result: any = undefined;
private handlers: Handlers = [];

constructor(executor: Executor<T>) {
try {
const resolve = (data: T) => {
this.changeStatus(this.FULFILLED, data);
}
const reject = (reason: any) => {
this.changeStatus(this.REJECTED, reason);
}
executor(resolve, reject);
} catch(error: any) {
this.changeStatus(this.REJECTED, error);
};
}

private changeStatus(state: Status, result: T) {
if (this.status !== this.PENDING) return;
this.status = state;
this.result = result;
this.run();
}

// 判断是否是 PromiseLike
private isPromiseLike(value: any) {
if (value !== null && (typeof value === "object" || typeof value === "function")) {
return typeof value.then === "function";
}
return false;
}

// 模拟微任务队列
private microTask(run: () => void) {
// node 环境:nextTick 的回调在本轮微任务执行完毕,下一轮宏任务执行开始之前执行
if (typeof process === "object" && typeof process.nextTick === "function") {
process.nextTick(run);
}

// 浏览器环境:MutationObserver 在监听到 dom 变动之后将回调函数放入微任务队列
else if (typeof MutationObserver === "function") {
const ob = new MutationObserver(run);
const textNode = document.createTextNode("1");
ob.observe(textNode, {
characterData: true
});
textNode.data = "2";
} else {
setTimeout(run, 0);
}
}

private runMicriTask(resolve: Resolve, reject: Reject, callback?: OnFulfilled | OnRejected) {
this.microTask(() => {
if (typeof callback === "function") {
try {
const res = callback(this.result);
if (this.isPromiseLike(res)) {
res.then(resolve, reject);
} else {
resolve(res)
}
} catch(error: any) {
reject(error);
}
} else {
const settled = this.status === this.FULFILLED? resolve : reject;
settled(this.result);
}
});
}

private run() {
// 状态没有立即发生改变或 then 方法没有传参
if (this.status === this.PENDING || !this.handlers.length) return;

while (this.handlers.length) {
const { onFulfilled, onRejected, resolve, reject } = this.handlers.shift()!;
if (this.status === this.FULFILLED) {
this.runMicriTask(resolve, reject, onFulfilled);
} else {
this.runMicriTask(resolve, reject, onRejected);
}
}
}

then<K>(onFulfilled?: OnFulfilled<T, K>, onRejected?: OnRejected<K>) {
return new MyPromise<K>((resolve, reject) => {
this.handlers.push({
onFulfilled,
onRejected,
resolve,
reject
});
this.run();
});
}
catch<K = never>(onRejected?: OnRejected<K>) {
return this.then<K>(undefined, onRejected);
}
finally(onFinally?: OnFinally) {
return this.then<T>(
() => {
onFinally && onFinally();
return undefined;
},
() => {
onFinally && onFinally();
return undefined;
},
)
}

static resolve<K = unknown>(value: K) {
return new MyPromise<K>((resolve => {
resolve(value);
}))
}
static reject<K = never>(reason?: any) {
return new MyPromise<K>((_, reject) => {
reject(reason);
});
}
}