手写 Promise
Promise 基本结构
Promise 的基本结构如下:
class MyPromise {
constructor(executor) {
// executor 是一个执行器函数,接受两个参数 resolve 和 reject
executor(this.resolve, this.reject);
}
// 定义 resolve 和 reject 方法,用于改变 Promise 的状态
resolve(data) {}
reject(err) {}
// 定义 then 方法,用于注册成功和失败的回调
then(onFulfilled, onRejected) {}
}2
3
4
5
6
7
8
9
10
11
12
Promise 状态
一个 Promise 的当前状态必须为以下三种状态中的一种:
- 挂起态/等待态
pending - 执行态/成功态
fulfilled - 拒绝态/失败态
rejected
初始化状态为 pending,当调用 resolve 方法时,状态变为 fulfilled,当调用 reject 方法时,状态变为 rejected。
状态一旦改变,就凝固了,不会再变。
// 定义状态常量
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
_status = PENDING; // 初始为挂起状态pending
_data = undefined; // 完成状态下的相关数据(结果)
_reason = undefined; // 失败状态下的相关数据(原因)
constructor(executor) {
// executor 是一个执行器函数,接受两个参数 resolve 和 reject
executor(this.resolve, this.reject);
}
// 定义 resolve 和 reject 方法,用于改变 Promise 的状态
resolve(data) {}
reject(err) {}
// 定义 then 方法,用于注册成功和失败的回调
then(onFulfilled, onRejected) {}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Promise 拒绝
reject 方法用于改变 Promise 的状态为失败,并传入一个失败的原因。 处于拒绝态时,promise 需满足以下条件:
- 不能迁移至其他任何状态
- 必须拥有一个不可变的据因
// 定义 rejectPromise 方法,用于改变 Promise 的状态为失败
function rejectPromise(promise, reason) {
if (promise._status !== PENDING) {
return; // 如果状态不是挂起态,直接返回
}
// 只有在挂起态时才能改变状态
promise._status = REJECTED; // 改变状态为失败
promise._reason = reason; // 保存失败原因
}
class MyPromise {
// 代码不变.....
// 定义 reject 方法,用于改变 Promise 的状态为失败
reject(err) {
rejectPromise(this, err);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Promise 成功
resolve 方法用于改变 Promise 的状态为成功,并传入一个成功的结果。
处于成功态时,promise 需满足以下条件:
- 不能迁移至其他任何状态
- 必须拥有一个不可变的值
promise 解决过程可以看以下流程图:
代码如下:
// 判断是否为对象
function isObject(val) {
return val !== null && typeof val === 'object';
}
// 判断是否为函数
function isFunction(val) {
return typeof val === 'function';
}
// 判断是否为 thenable 对象
function isThenable(x) {
return (isObject(x) || isFunction(x)) && isFunction(x.then);
}
// 定义 resolvePromise 方法,用于改变 Promise 的状态为成功
function resolvePromise(promise, x) {
if (promise._status !== PENDING) {
return; // 如果状态不是挂起态,直接返回
}
// 如果x是一个thenable对象
if (isThenable(x)) {
// 1.如果 x 是 promise 本身,以 TypeError 为据因拒绝执行 promise
if (x === promise) {
rejectPromise(promise, new TypeError('Chaining cycle detected for promise #<MyPromise>'));
return;
}
// 2.promise 吸收 x 的状态
x.then(
(data) => {
// 如果 x 是成功状态,以相同的值完成 promise
resolvePromise(promise, data);
},
(err) => {
// 如果 x 是失败状态,以相同的据因拒绝 promise
rejectPromise(promise, err);
}
);
} else {
promise._status = FULFILLED; // 改变状态为成功
promise._data = x; // 保存成功结果
}
}
class MyPromise {
// 代码不变.....
// 定义 resolve 方法,用于改变 Promise 的状态为成功
resolve(data) {
resolvePromise(this, data);
}
}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
注意:
ES6 的 Promise,状态吸收是放在微队列里的,使用queueMicrotask方法可以把任务添加到微队列里去执行。
queueMicrotask(() => {
x.then(
(data) => {
resolvePromise(promise, data);
},
(err) => {
rejectPromise(promise, err);
}
);
});2
3
4
5
6
7
8
9
10
then 方法
then 方法接收两个参数,第一个参数是fulfilled状态的回调,第二个参数是rejected状态的回调:
promise.then(onFulfilled, onRejected){}一个 promise 必须有 then 方法,then 方法必须返回一个新的 promise。
class MyPromise {
// 代码不变.....
// 定义 then 方法,用于注册成功和失败的回调
then(onFulfilled, onRejected) {
// 创建一个新的 Promise 对象,用于返回
const promise2 = new MyPromise((resolve, reject) => {});
return promise2;
}
}2
3
4
5
6
7
8
9
10
提示
then 方法处理的核心问题就两个:
- 确定
then的onFulfilled和onRejected在什么时候调用 - 确定
then返回的新的promise的状态是什么
then 可以调用多次,所以需要数组来存储多个回调
// 处理已决后的回调函数
// 将 curPromise 中的所有回调进行处理
function callSettledCallbacks(curPromise) {}
class MyPromise {
// then可以调用多次,所以需要数组来存储多个回调
_settledCallbacks = []; // 当前 Promise 实例已决后要进行的后续处理
// 代码不变.....
// 定义 then 方法,用于注册成功和失败的回调
then(onFulfilled, onRejected) {
// 创建一个新的 Promise 对象,用于返回
const promise2 = new MyPromise((resolve, reject) => {});
this._settledCallbacks.push({
onFulfilled,
onRejected,
promise2 // 后续处理的 Promise 实例
});
return promise2;
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
存储后,在适当的时机去调用这些回调:
- 当
promise状态变化时(从pending到fulfilled或rejected):javascript// 在这段代码中,因为resolve是异步执行的,所以会先执行then去注册回调,后执行resolve去变化状态 // 所以在状态变化后,会调用这些then注册的回调 const promise = new MyPromise((resolve, reject) => { setTimeout(() => { resolve(123); }, 1000); }); promise.then( (data) => { console.log('success', data); }, (err) => { console.log('error', err); } );1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 - 当
then方法被调用时:javascript根据以上时机去调用:// 在这段代码中,因为resolve是同步执行的,所以会先执行resolve去变化状态,后执行then去注册回调 // 所以在then注册的回调时,如果状态已经变化了,就会立即调用回调 const promise = new MyPromise((resolve, reject) => { resolve(123); }); promise.then( (data) => { console.log('success', data); // 123 }, (err) => { console.log('error', err); } );1
2
3
4
5
6
7
8
9
10
11
12
13
// 代码不变
// 定义 rejectPromise 方法,用于改变 Promise 的状态为失败
function rejectPromise(promise, reason) {
if (promise._status !== PENDING) {
return; // 如果状态不是挂起态,直接返回
}
// 只有在挂起态时才能改变状态
promise._status = REJECTED; // 改变状态为失败
promise._reason = reason; // 保存失败原因
// 调用已决后的回调函数
callSettledCallbacks(promise);
}
// 定义 resolvePromise 方法,用于改变 Promise 的状态为成功
function resolvePromise(promise, x) {
if (promise._status !== PENDING) {
return; // 如果状态不是挂起态,直接返回
}
// 如果x是一个thenable对象
if (isThenable(x)) {
// 代码不变
} else {
promise._status = FULFILLED; // 改变状态为成功
promise._data = x; // 保存成功结果
// 调用已决后的回调函数
callSettledCallbacks(promise);
}
}
// 处理已决后的回调函数
// 将 curPromise 中的所有回调进行处理
function callSettledCallbacks(curPromise) {}
class MyPromise {
// then可以调用多次,所以需要数组来存储多个回调
_settledCallbacks = []; // 当前 Promise 实例已决后要进行的后续处理
// 代码不变.....
// 定义 then 方法,用于注册成功和失败的回调
then(onFulfilled, onRejected) {
// 创建一个新的 Promise 对象,用于返回
const promise2 = new MyPromise((resolve, reject) => {});
this._settledCallbacks.push({
onFulfilled,
onRejected,
promise2 // 后续处理的 Promise 实例
});
// 调用已决后的回调函数
callSettledCallbacks(this);
return promise2;
}
}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
onFulfilled 和 onRejected 的处理过程可以用以下流程图表示:
步骤详解
onFulfilled和onRejected都是可选参数。- 如果
onFulfilled不是函数,必须忽略它(即状态穿透)。 - 如果
onRejected不是函数,必须忽略它(即状态穿透)。
- 如果
- 如果
onFulfilled是函数,- 当
promise执行结束后(状态为fulfilled) 时,必须异步调用onFulfilled,并将promise的结果作为参数。 - 在
promise执行结束前,onFulfilled不可被调用。 - 调用次数不能超过一次。
- 当
- 如果
onRejected是函数,- 当
promise执行结束后(状态为rejected) 时,必须异步调用onRejected,并将promise的据因作为参数。 - 在
promise执行结束前,onRejected不可被调用。 - 调用次数不能超过一次。
- 当
在 Promise/A+ 规范中,要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。这个事件队列可以采用"宏队列(macro-task)"机制或者"微队列(micro-task)"机制来实现。而在 ES6 中是放在了微队列,所以我们按照 ES6 的处理来实现。
// 处理已决后的回调函数
// 将 curPromise 中的所有回调进行处理
function callSettledCallbacks(curPromise) {
if (curPromise._status === PENDING) {
return;
}
// 必须在已决之后处理
// 获取所有的回调
const settledCallbacks = curPromise._settledCallbacks;
// 将后续的处理放到微队列
queueMicrotask(() => {
// 循环所有已决后的回调,并把执行之后的回调清除(每个回调只运行一次)
while (settledCallbacks.length) {
// 取出第一个回调
const callback = settledCallbacks.shift();
const { onFulfilled, onRejected, promise2 } = callback;
// 当前的Promise完成,但完成的回调不是函数,则状态穿透
if (!isFunction(onFulfilled) && curPromise._status === FULFILLED) {
// 状态穿透,把curPromise的结果传递给promise2
resolvePromise(promise2, curPromise._data);
continue;
}
// 当前的Promise失败,但失败的回调不是函数,则状态穿透
if (!isFunction(onRejected) && curPromise._status === REJECTED) {
// 状态穿透,把curPromise的据因传递给promise2
rejectPromise(promise2, curPromise._reason);
continue;
}
let resule;
try {
result =
curPromise._status === FULFILLED
? onFulfilled(curPromise._data)
: onRejected(curPromise._reason);
} catch (err) {
// 处理回调函数执行时的错误
rejectPromise(promise2, err);
}
// 处理正常的回调返回值
resolvePromise(promise2, result);
}
});
}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
至此,符合 Promise/A+ 规范的 Promise 就完成了,至于其他的方法,如 catch、finally 等,都是基于 then 方法的封装。
完整代码
// 定义状态常量
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
// 判断是否为对象
function isObject(val) {
return val !== null && typeof val === 'object';
}
// 判断是否为函数
function isFunction(val) {
return typeof val === 'function';
}
// 判断是否为 thenable 对象
function isThenable(x) {
return (isObject(x) || isFunction(x)) && isFunction(x.then);
}
// 定义 rejectPromise 方法,用于改变 Promise 的状态为失败
function rejectPromise(promise, reason) {
if (promise._status !== PENDING) {
return; // 如果状态不是挂起态,直接返回
}
// 只有在挂起态时才能改变状态
promise._status = REJECTED; // 改变状态为失败
promise._reason = reason; // 保存失败原因
// 调用已决后的回调函数
callSettledCallbacks(promise);
}
// 定义 resolvePromise 方法,用于改变 Promise 的状态为成功
function resolvePromise(promise, x) {
if (promise._status !== PENDING) {
return; // 如果状态不是挂起态,直接返回
}
// 如果x是一个thenable对象
if (isThenable(x)) {
// 1.如果 x 是 promise 本身,以 TypeError 为据因拒绝执行 promise
if (x === promise) {
rejectPromise(promise, new TypeError('Chaining cycle detected for promise #<MyPromise>'));
return;
}
// 2.promise 吸收 x 的状态
x.then(
(data) => {
// 如果 x 是成功状态,以相同的值完成 promise
resolvePromise(promise, data);
},
(err) => {
// 如果 x 是失败状态,以相同的据因拒绝 promise
rejectPromise(promise, err);
}
);
} else {
promise._status = FULFILLED; // 改变状态为成功
promise._data = x; // 保存成功结果
// 调用已决后的回调函数
callSettledCallbacks(promise);
}
}
// 处理已决后的回调函数
// 将 curPromise 中的所有回调进行处理
function callSettledCallbacks(curPromise) {
if (curPromise._status === PENDING) {
return;
}
// 必须在已决之后处理
// 获取所有的回调
const settledCallbacks = curPromise._settledCallbacks;
// 将后续的处理放到微队列
queueMicrotask(() => {
// 循环所有已决后的回调,并把执行之后的回调清除(每个回调只运行一次)
while (settledCallbacks.length) {
// 取出第一个回调
const callback = settledCallbacks.shift();
const { onFulfilled, onRejected, promise2 } = callback;
// 当前的Promise完成,但完成的回调不是函数,则状态穿透
if (!isFunction(onFulfilled) && curPromise._status === FULFILLED) {
// 状态穿透,把curPromise的结果传递给promise2
resolvePromise(promise2, curPromise._data);
continue;
}
// 当前的Promise失败,但失败的回调不是函数,则状态穿透
if (!isFunction(onRejected) && curPromise._status === REJECTED) {
// 状态穿透,把curPromise的据因传递给promise2
rejectPromise(promise2, curPromise._reason);
continue;
}
let resule;
try {
result =
curPromise._status === FULFILLED
? onFulfilled(curPromise._data)
: onRejected(curPromise._reason);
} catch (err) {
// 处理回调函数执行时的错误
rejectPromise(promise2, err);
}
// 处理正常的回调返回值
resolvePromise(promise2, result);
}
});
}
class MyPromise {
_status = PENDING; // 初始为挂起状态pending
_data = undefined; // 完成状态下的相关数据(结果)
_reason = undefined; // 失败状态下的相关数据(原因)
// then可以调用多次,所以需要数组来存储多个回调
_settledCallbacks = []; // 当前 Promise 实例已决后要进行的后续处理
constructor(executor) {
// executor 是一个执行器函数,接受两个参数 resolve 和 reject
executor(this.resolve, this.reject);
}
// 定义 reject 方法,用于改变 Promise 的状态为失败
reject(err) {
rejectPromise(this, err);
}
// 定义 resolve 方法,用于改变 Promise 的状态为成功
resolve(data) {
resolvePromise(this, data);
}
// 定义 then 方法,用于注册成功和失败的回调
then(onFulfilled, onRejected) {
// 创建一个新的 Promise 对象,用于返回
const promise2 = new MyPromise((resolve, reject) => {});
this._settledCallbacks.push({
onFulfilled,
onRejected,
promise2 // 后续处理的 Promise 实例
});
// 调用已决后的回调函数
callSettledCallbacks(this);
return promise2;
}
}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
146
147

