手写promise

手写Promise

ECMAscript 6 原生提供了 Promise 对象。

Promise 对象代表了未来将要发生的事件,用来传递异步操作的消息。

Promise 对象有以下两个特点:

对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:

  • pending: 初始状态,不是成功或失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
// 参考PromiseA+规范,同时根据es自身的实现逻辑添加其他操作
// https://promisesaplus.com/

// 状态字符串(常量)
const PROMISE_STATUS_PENDING = 'pending';
const PROMISE_STATUS_FULFILLED = 'fulfilled';
const PROMISE_STATUS_REJECTED = 'rejected';

// 工具函数
// 公共逻辑抽取
function execFunctionWithCatchError(fn, data, resolve, reject) {
try {
const result = fn(data);
resolve(result)
} catch (e) {
reject(e);
}
}

class MyPromise {
constructor(executor) {
this.status = PROMISE_STATUS_PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfilledFns = [];
this.onRejectedFns = [];
// 调用resolve状态会改变为fulfilled(resolved)
const resolve = (value) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 加入到微任务队列(异步,不影响主线程)
queueMicrotask(() => {
// queueMicrotask是异步代码,此处需要重新判断状态
if (this.status !== PROMISE_STATUS_PENDING) return;
this.status = PROMISE_STATUS_FULFILLED;
// pending ==> fulfilled
// 保存fulfilled的值
this.value = value;
// 执行then方法的onFulfilled回调(第一个回调)
this.onFulfilledFns.forEach(fn => fn(this.value));
});
}
};
// 调用reject状态会改变为rejected
const reject = (reason) => {
if (this.status === PROMISE_STATUS_PENDING) {
// 加入到微任务队列(异步,不影响主线程)
queueMicrotask(() => {
// queueMicrotask是异步代码,此处需要重新判断状态
if (this.status !== PROMISE_STATUS_PENDING) return;
this.status = PROMISE_STATUS_REJECTED;
// pending ==> rejected
// 保存rejected的值
this.reason = reason;
// 执行then方法的onRejected回调(第二个回调)
this.onRejectedFns.forEach(fn => fn(this.reason));
})
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
// 如果reject或者throw异常,但是没有传入then的第二个回调,需要把这个异常或者reason一直抛直到被catch捕获
onRejected = onRejected || (err => { throw err });
// 如果resolve没有传入,则会造成value没有处理的回调,会造成链式调用的断层
onFulfilled = onFulfilled || (value => value);
// then方法返回一个MyPromise对象
// resolve的value或者reject的reason都可以被认为是then方法的返回值
// 使用resolve还是reject取决于传入的onFulfilled, onRejected的返回值
return new MyPromise((resolve, reject) => {
// 1.如果执行then时,状态已经确定了,直接执行onFulfilled或onRejected,不用添加到执行队列里面
if (this.status === PROMISE_STATUS_FULFILLED && typeof onFulfilled === 'function') {
// try {
// const value = onFulfilled(this.value);
// resolve(value);
// } catch (error) {
// reject(error);
// }

// 公共抽取
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
}
if (this.status === PROMISE_STATUS_REJECTED && typeof onRejected === 'function') {
// try {
// const reason = onRejected(this.reason);
// // 因为then方法的第二个回调也是返回一个MyPromise对象,所以此处也需要调用resolve而不是reject
// resolve(reason)
// } catch (error) {
// reject(error);
// }

// 公共抽取
execFunctionWithCatchError(onRejected, this.reason, resolve, reject);
}
// 2.如果执行then时,状态还是pending,添加到执行队列里面
// 因为promise的then可链式调用,保存onFulfilled, onRejected由数组来保存
if (this.status === PROMISE_STATUS_PENDING) {
if (typeof onFulfilled === 'function') {
this.onFulfilledFns.push(() => {
// try {
// const value = onFulfilled(this.value);
// resolve(value);
// } catch (error) {
// reject(error);
// }

// 公共抽取
execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
})
}
if (typeof onRejected === 'function') {
this.onRejectedFns.push(() => {
// try {
// const reason = onRejected(this.reason);
// resolve(reason);
// } catch (error) {
// reject(error);
// }
// 公共抽取
execFunctionWithCatchError(onRejected, this.reason, resolve, reject);
});
}
}
})
}
// catch方法和finaly方法属于es6特有,不属于promiseA+规范
catch(onRejected) {
return this.then(undefined, onRejected);
}
finally(onFinally) {
this.then(() => {
onFinally()
}, () => {
onFinally()
});
}
// 类方法
static resolve(value) {
return new MyPromise((resolve) => {
resolve(value);
})
}

static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
})
}
// all
// 只有所有的promise都resolve,才resolve
// 有一个promise失败,就reject
static all(promises) {
return new MyPromise((resolve, reject) => {
const values = []
let count = 0
promises.forEach((promise, index) => {
promise.then(res => {
// 此处保证了resolve返回的顺序和传入的自定义promise顺序一致
values[index] = res
count++;
if (count === promises.length) {
resolve(values);
}
}, err => {
reject(err)
});
})
});
}
// allSettled
// 不执行reject,只执行resolve
// 返回的promise的包括status和value(reason)
static allSettled(promises) {
return new MyPromise((resolve) => {
const values = [];
let count = 0;
promises.forEach((promise, index) => {
promise.then((res) => {
count++;
values[index] = {
status: 'fulfilled',
value: res
};
if (count === promises.length) {
resolve(values)
}
}, (err) => {
count++
values[index] = {
status: 'rejected',
reason: err
};
if (count === promises.length) {
resolve(values)
}
})
})
});
}
// race
// 返回最先完成resolve或者reject的promise的执行结果
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach((promise) => {
promise.then(resolve, reject)
})
})
}
// any
// 返回第一个resolve的promise
// 若所有的promise都reject,则reject(new AggregateError(reasons))
static any(promises) {
return new MyPromise((resolve, reject) => {
const reasons = [];
let count = 0;
promises.forEach((promise, index) => {
promise.then(resolve, err => {
count++;
reasons[index] = err;
if (count === promises.length) {
// reject('all failed');
reject(new AggregateError(reasons));
}
})
})
})
}
}






let promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject(1)
}, 4000);
})


let promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {


reject(2)
}, 5000);
})
let promise3 = new MyPromise((resolve, reject) => {
setTimeout(() => {

reject(3)
}, 100);
})


MyPromise.any([promise, promise2, promise3]).then(res => {
console.log(res);
}, (err) => {
console.log('err', err.errors);
})