前言

今天翻之前的面经时看到了虎牙的面试官问过我怎么实现promise,当时我没答出then的实现(有一说一我是没想到会实习会问这个的orz)

那今天就顺手回顾一下

实现思路

完整的实现思路我在我之前的博客写过了

可以看这里:https://www.sakura-snow.com/archives/180

这里只对关键部分的代码做介绍

先贴代码,你可以先翻到下面看思路

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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
const MyPromise = (function () {
// 对应的三个状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// 当前promise的数据
const PromiseValue = Symbol("PromiseValue");
// 当前promise的状态
const PromiseStatus = Symbol("PromiseStatus");
// 成功时要执行的回调函数列表
const onFulfilledList = Symbol("onFulfilledList");
// 失败时要执行的回调函数列表
const onRejectedList = Symbol("onRejectedList");
// 更新状态的函数
const updateStatus = Symbol("updateStatus");
// 把任务添加到任务队列里异步执行的函数
const executeAsync = Symbol("executeAsync");
// 创建串联的promise
const createLinkPromise = Symbol("createLinkPromise");
// 后续处理的通用函数
const settleHandle = Symbol("settleHandle");
// 执行then绑定的回调, 同时try catch
const execute = Symbol("execute");
// 默认的OnFulfilled
const defaultOnFulfilled = function (data) {return data};
// 默认的OnRejected
const defaultOnRejected = function (err) {throw new Error(err)};
return class MyPromise{
/**
* 异步执行回调函数
* @param handler 回调函数
* @param arg 传递给回调函数的参数
*/
[executeAsync] (handler, ...arg) {
setTimeout(function () {
handler(...arg);
}, 0)
}
/**
* 更新状态
* @param newStatus 新的状态
* @param newValue promise新的数据
* @param executeQueue 要执行的回调列表
*/
[updateStatus] (newStatus, newValue, executeQueue) {
// 如果状态不是pending, 就返回
if (this[PromiseStatus] !== PENDING) return;
// 把当前promise的状态改成新的状态
this[PromiseStatus] = newStatus;
// 把当前promise的值更新为新的值
this[PromiseValue] = newValue;
// 把回调列表里的函数取出来依次执行
executeQueue.forEach((handler) => {
// 异步执行then中注册的回调
this[executeAsync] (handler, newValue);
})
}
/**
* 处理then传入的回调函数
* @param handler 回调函数
* @param immediatelyStatus 立即推入任务队列执行的状态
* @param queue 如果不立即执行,保存的队列
*/
[settleHandle] (handler, immediatelyStatus, queue) {
// 如果传入的不是一个函数就返回
if (typeof handler !== "function") return;
// 判断是不是立刻执行的状态
if (this[PromiseStatus] === immediatelyStatus) {
// 是就立刻推入异步执行队列
this[executeAsync] (handler, this[PromiseValue]);
} else {
// 不是就放到回调列表中
queue.push(handler);
}
}
/**
* 执行回调,获取返回值,如果报错就执行reject
* @param data 传给handler的数据
* @param handler 要在try catch中执行的回调函数
* @param resolve 下个promise的resolve
* @param reject 下个promise的reject
*/
[execute] (data, handler, resolve, reject) {
try {
const result = handler(data);
// 如果返回值是promise
if (result instanceof MyPromise) {
// 用then注册回调
result.then((data) => {
// 把回调收到的数据转发给下个promise
resolve(data);
}, (err) => {
reject(err);
})
}else {
// 如果返回值不是promise,直接传递就行
resolve(result);
}
}catch (e) {
reject(e);
}
}
/**
* 用于创建then返回的新promise
* @param onFulfilled then里传入的onFulfilled函数
* @param onRejected then里传入的onRejected函数
*/
[createLinkPromise] (onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
// 在这里调用[settleHandle]就能处理then传入的回调了
this[settleHandle]((data) => {
this[execute](data, onFulfilled, resolve, reject);
}, RESOLVED, this[onFulfilledList]);
this[settleHandle]((err) => {
this[execute](err, onRejected, resolve, reject);
}, REJECTED, this[onRejectedList]);
});
}
then(onFulfilled = defaultOnFulfilled, onRejected = defaultOnRejected) {
return this[createLinkPromise](onFulfilled, onRejected);
}
catch(onRejected) {
return this.then(defaultOnFulfilled, onRejected);
}
finally(handler) {
return this.then(handler, handler);
}
constructor(executor) {
// 初始化当前promise的状态和数据
this[PromiseStatus] = PENDING;
this[PromiseValue] = undefined;
// 初始化回调列表
this[onFulfilledList] = [];
this[onRejectedList] = [];
// 定义resolve函数
const resolve = (data) => {
this[updateStatus](RESOLVED, data, this[onFulfilledList]);
};
// 定义reject函数
const reject = (reason) => {
this[updateStatus](REJECTED, reason, this[onRejectedList]);
};
try {
// 同步执行new Promise时传入的函数
executor && executor(resolve, reject);
}catch (e) {
// 如果捕获到异常就调用reject
reject(e);
}
}
static all(promises) {
// promise数组的长度
let length = promises.length;
// 创建用于保存结果的数组
let resultArr = new Array(length);
// 已经处于resolved状态的promise的个数
let count = 0;
return new MyPromise((resolve, reject) => {
promises.map((promise, index) => {
// 给每个promise都注册回调
promise.then((data) => {
// 处于resolve状态后, 把count加1
count ++;
// 保存数据到数组中
resultArr[index] = data;
// 如果全部处于resolved状态
if (count >= length) {
// 就把返回的promise变成resolved状态
resolve(resultArr);
}
}, (err) => {
// 有任一个处于rejected状态
// 就把返回的promise置为rejected状态
reject(err);
})
})
})
}
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach((promise) => {
// 任意一个修改状态后就修改返回的promise的状态
promise.then((data) => {
resolve(data);
}, (err) => {
reject(err);
})
})
});
}
// 创建一个处于resolve状态的promise
static resolve(data) {
if (data instanceof MyPromise) return data;
return new MyPromise((resolve) => {
resolve(data);
})
}
// 创建一个处于reject状态的promise
static reject(err) {
if (err instanceof MyPromise) return err;
return new MyPromise((resolve, reject) => {
reject(err);
})
}
}
})();

constructor

image-20210207215054879

这里做了两件事

  • 数据初始化
  • 同步执行传入的函数

这里有必要介绍一下resovlereject的实现

先来看一看updateStatus是什么

image-20210207215259540

updateStatus做的事很简单

  • 更新当前Promise的状态
  • 更新状态后把对应队列中的回调函数取出来执行

then

我们先不考虑then串联的情况,在只能then一次时,then需要做下面的处理

  • 如果Promise的状态已经是resolved或者rejected,立即执行回调
  • 否则,把回调保存到相应的队列中,等待Promise的状态变化再执行

值的注意的是,这里的this,是指向前一个Promise,而不是新返回的那个Promise

image-20210207225010660

image-20210207225530004

我选中的函数就是推入到队列中的回调

image-20210207230352588

串联then

串联then是通过下面的过程

  • 前一个Promise变为resolved或者rejected状态时,会调用添加到队列里的函数
  • 执行回调函数时,可以拿到Promise.then里传入的onFulfilledonRejected还有resolvereject
    • onFulfilledonRejected是前一个Promise变为变为resolved或者rejected状态时要调用的回调函数
    • resolvereject用于改变返回的Promise的函数的状态(就是constructor)里的resolvereject
  • 如果onFulfilledonRejected返回一个普通值,直接调用resolved或者rejected并且传入返回值就可以了
  • 如果onFulfilledonRejected返回的是一个Promise,就要给onFulfilledonRejected返回的Promise通过then注册一个回调(可以通过注册的回调中看出,它会拿到返回的Promise调用resolve或者reject时传入的值)

看不懂嘛,其实简单来说就是

thenonFulfilled会被包裹一层注册到前一个Promise的队列中,前一个Promise的状态变化后执行被包裹的函数,被包裹的函数会执行onFulfilled,然后处理onFulfilled返回值并且调用下一个Promiseresolve

image-20210207230702198

后记

其实你会发现,整个实现流程中,你传入的函数并不是直接执行的,都是会经过相应地包装,这样Promise内部就可以对函数进行各种各样的控制,比如说

  • 对返回值进行监测
  • 对函数进行try catch

这个故事告诉我们,在计算机中,没有什么是不能多加一个中间层搞定的,如果有,就多加几个

啊哈

不管怎么说,思路还是比较清晰的,虽然代码实现略微复杂,但是思路在那,慢慢看就可以看懂了