-
Notifications
You must be signed in to change notification settings - Fork 0
/
my-promise.js
303 lines (303 loc) · 16.1 KB
/
my-promise.js
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
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/**
* Promise 遵循原则:
*
* from: https://juejin.cn/post/7043758954496655397#heading-17
*
* 我们都知道 Js 是单线程都,但是一些高耗时操作就带来了进程阻塞问题。为了解决这个问题,Js 有两种任务的执行模式:同步模式(Synchronous)和异步模式(Asynchronous)。
* 在异步模式下,创建异步任务主要分为宏任务与微任务两种。ES6 规范中,宏任务(Macrotask) 称为 Task, 微任务(Microtask) 称为 Jobs。
* 宏任务是由宿主(浏览器、Node)发起的,而微任务由 JS 自身发起。
*
* 1. Promise对象代表着一个异步操作,它必然处于以下三种状态:
* - pending 等待
* - fulfilled 已完成 --> 执行 resolve()
* - rejected 已拒绝 --> 执行reject() / 抛出错误
* 当Promise处于 fulfilled / rejected 后,将无法变更状态
*
* 2. Promise状态运转流程: 当 promise 被调用后,它会以处理中状态 (pending) 开始, 最终会以被解决状态 (fulfilled) 或 被拒绝状态 (rejected) 结束,并在完成时调用相应的回调函数(传给 then 和 catch)。
*
* 3. Promise.then回调执行时机: 在本轮 事件循环 运行完成之前,Promise.then 的回调函数是不会被调用的 --> PS: 需要注意的是 Promise 本身内部接受的函数参数 (resolve: Promise.resolve, reject: Promise.reject) => void; 是同步进行的
*
* 4. thenable对象: 指的是具有then方法的对象,Promise的一些静态方法会自动像解析Promise一样对其进行解析运行
*
* 5. Promise.resolve(T): 将会返回一个 Promise 对象,其中 参数 T 有以下几种情况
* 1) T = Promise实例: 则原封不动返回这个 Promise 实例
* 2)T = thenable对象: Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法
* 3) T = 不是具有then方法的对象,或根本就不是对象(基础数据类型 + 没有then方法的对象): 返回一个新的 Promise 对象,状态为resolved,将参数 T 传递到下一个then
* 4) T = 空,不传参数: 直接返回一个resolved状态的 Promise 对象
*
* 6. Promise.reject(T): 将返回一个新的 Promise 实例,该实例的状态为rejected
* 1) 其中参数T,将会原封不动的作为 reject 理由返回,这一点与Promise.resolve不同 ==>
* eg: Promise.reject({ then: (resolve, reject) => reject(123) })
* .catch(rejectReason => console.log(rejectReason)); // { then: (resolve, reject) => reject(123) } 而不是 123
*
* 7. Promise.prototype.then(T): 它的作用是为 Promise 实例添加状态改变时的回调函数
* 1) then方法的第一个参数是resolved状态的回调函数。如果该参数不是函数,则会在内部被替换为 (x) => x,即原样返回 promise 最终结果的函数;
* 2) 第二个参数(可选)是rejected状态的回调函数。如果该参数不是函数,则会在内部被替换为一个 "Thrower" 函数 (it throws an error it received as argument)。
* 3) then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
* - 1. 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
* - 2. 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
* - 3. 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因
* - 4. 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)
* - 如果x与promise(promise2)指向同一个对象,返回TypeError,拒绝执行(防止循环引用)
* - 如果x是一个Promise实例,则令promise2接收x的状态(解开x.then返回值,将其递归返回 ==> 类似 resolvePromise(promise2, await promise2, resolve, reject))
* - 如果x为对象/函数,将其当作thenable执行,与上同
* - 如果x不为对象/函数,直接resolve/reject返回x
*
* 8. Promise.prototype.catch(): catch() 方法返回一个Promise,并且处理拒绝的情况。
* 1) 它的行为与调用Promise.prototype.then(undefined, onRejected) 相同,可以将其视作这种形式的语法糖
* 2) 如果 Promise 状态已经变成resolved,再抛出错误是无效的。`new Promise((s, j) => { s(123); j(456) })` ==> j(456) 是无效的,不会被错误捕获
* 3) Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。所以通常,一串Promise链条只需要在最末尾有一个 catch 处理错误即可。
* 4) 当一个Promise链,没有错误处理时,将会将错误抛出到顶层 `Unhandled Promise Rejection Warning.`
*
* 9. 链式调用: Promise.prototype.then/catch 始终会返回一个新的Promise实例对象,这使得我们可以链式调用(一定程度缓解了原来异步回调地狱的问题) Promise.resolve().then(...).then(...).then(...)...
*
* 10. Promise.prototype.finally(): 与 try/catch/finally 中的 finally 一个道理,finally 无论结果是 fulfilled 还是 rejected 都会被执行。
* 1) 由于无法知道promise的最终状态,所以finally的回调函数中不接收任何参数,它仅用于无论最终结果如何都要执行的情况。
*
* 11. Promise.all(PromiseInstance[]): 并发Promise函数
* 1) 同时并发多个Promise,并在所有的Promise实例都 fulfilled 后,调用其thenable
* 2) 当某个并发的Promise,状态为 reject 时,将终止 Promise.all,并将控制权转交后续的 catch 处理
* 3) 后续 thenable 接收参数为 Promise.all() 中每个 Promise 结果的数组,按照原顺序排序
*
* 12. Promise.race(PromiseInstance[]): 竞态Promise函数
* 1) 同时竞态执行多个Promise,只要其中一个 Promise 状态变为完成(fulfilled / rejected),将结束运行
* 2) 一般用作异步操作超时处理
*
* 13. Promise.allSettled(): 并发Promise,但与Promise.all不同的是,rejected状态也不会直接结束
* 1) 同时并发多个Promise,并在所有的Promise实例都 fulfilled 或 rejected 后,调用其thenable
* 2) 与Promise.all不同的是,其返回值,将被固定数据结构包装一层 `{ status: 'fulfilled' | 'rejected'; value?: any; reason?: any }[]` fulfilled 状态 value 有值,rejected 下 reason 有值。
*/
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _MyPromise_instances, _MyPromise_clearCallbacks;
var PromiseStatus;
(function (PromiseStatus) {
PromiseStatus["PENDING"] = "pending";
PromiseStatus["FULFILLED"] = "fulfilled";
PromiseStatus["REJECTED"] = "rejected";
})(PromiseStatus || (PromiseStatus = {}));
// 返回callback的异步执行函数版本
function nextTick(callback) {
return () => setTimeout(() => callback && callback());
}
/**
* resolve()、reject() 进行改造增强 针对resolve()和reject()中不同值情况 进行处理(参考Promise/A+规范 [[Resolve]](promise2, x) 解决过程)
* 正常处理流程即用resolve返回(onRejected的返回值按照规范也会作为fulfilledValue,故也用resolve返回)
* @param {MyPromise<T>} promise2 promise1.then方法返回的新的promise对象
* @param {O} x promise1中onFulfilled或onRejected的返回值
* @param {(value: any) => any} resolve promise2的resolve方法
* @param {(value: any) => any} reject promise2的reject方法
*/
function resolvePromise(promise2, x, resolve, reject) {
// 在onFulfilled中返回自身的情况,将造成循环引用
if (promise2 === x) {
throw new TypeError('检测到Promise链循环引用');
}
// x 为 promise 对象的情况
if (x instanceof MyPromise) {
x.then((xValue) => resolvePromise(promise2, xValue, resolve, reject), reject);
return;
}
// x为对象/函数,将其当作thenable尝试执行
if (typeof x !== null &&
(typeof x === 'object' || typeof x === 'function')) {
let then;
try {
then = x.then;
}
catch (e) {
return reject(e);
}
if (typeof then !== 'function') {
resolve(x);
}
else {
// then为函数,执行,将其当作then执行
// 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
let called = false;
try {
then.call(x, (thenValue) => {
if (called) {
return;
}
called = true;
resolvePromise(promise2, thenValue, resolve, reject); // 递归,确保thenValue符合规则
}, (thenReason) => {
if (called) {
return;
}
called = true;
reject(thenReason); // 拒绝情况,可直接返回
});
}
catch (e) {
if (called) {
return;
}
called = true;
reject(e);
}
}
return;
}
// 其他x非对象/函数情况
resolve(x);
}
class MyPromise {
constructor(executor) {
_MyPromise_instances.add(this);
this.PromiseResult = undefined;
this.onFulfilledCallbacks = []; // 成功回调保存: then执行时为pending状态,先将回调保存,等待resolve执行后再取出执行 | 用数组缓存,也同时实现了一个Promise能被多次then
this.onRejectedCallbacks = []; // 失败回调保存: ~
// 1. 初始化状态pending
this.PromiseState = PromiseStatus.PENDING;
// 2. then回调保存
// 3. 执行Promise回调(PS:这里用bind绑定实例的this环境,因为二者要作为参数传递到外部【executor】函数作用域环境中调用,会丢失class实例的this)
try {
executor(this.resolve.bind(this), this.reject.bind(this));
}
catch (err) {
// executor执行抛出的错误,自动当作reject处理
this.reject(err);
}
}
resolve(value) {
// resolve后,将pending状态改为fulfilled(PS:这里存在判断而非直接修改,遵循上面原则 1. 当状态非 pending 后,将无法在修改状态)
if (this.PromiseState === PromiseStatus.PENDING) {
this.PromiseState = PromiseStatus.FULFILLED;
this.PromiseResult = value;
this.onFulfilledCallbacks.forEach((fn) => fn && fn(value)); // 执行已订阅的resolve回调
__classPrivateFieldGet(this, _MyPromise_instances, "m", _MyPromise_clearCallbacks).call(this);
}
}
reject(reason) {
if (this.PromiseState === PromiseStatus.PENDING) {
this.PromiseState = PromiseStatus.REJECTED;
this.PromiseResult = reason;
this.onRejectedCallbacks.forEach((fn) => fn && fn(reason));
__classPrivateFieldGet(this, _MyPromise_instances, "m", _MyPromise_clearCallbacks).call(this);
}
}
/**
* then方法链:缓解了历史的回调地狱问题
* 7.3 then需要返回一个新的Promise, 其执行过程 [[Resolve]](promise2, x)
* 7.3.1/2/3. 如果onFulfilled/onRejected执行过程抛出异常e,则promise2必须拒绝执行(reject),返回拒绝原因
* 7.3.4. 规定了onFulfilled/onRejected返回值x不同情况下的不同处理逻辑
* @param onFulfilled
* @param onRejected
* @returns
*/
then(onFulfilled, onRejected) {
const newPromise = new MyPromise((resolve, reject) => {
const execOnFulfilled = () => {
try {
if (typeof onFulfilled !== 'function') {
resolve(this.PromiseResult);
}
else {
const x = onFulfilled(this.PromiseResult);
resolvePromise(newPromise, x, resolve, reject);
}
}
catch (e) {
reject(e);
}
};
const execOnRejected = () => {
try {
if (typeof onRejected !== 'function') {
reject(this.PromiseResult);
}
else {
const x = onRejected(this.PromiseResult);
// 这里onRejected的返回值依旧放入resolvePromise,因为规范上来说,onRejected的返回值 (非Promise.reject/error/执行错误情况) 会作为promise2的 fulfilledValue
// 故此 (new Promise((s, r) => r('123'))).then(null, r => r).then(res => 'get:' + res) ==> 'get:123'
resolvePromise(newPromise, x, resolve, reject);
}
}
catch (e) {
reject(e);
}
};
// 调用时,pending:回调保存起来,直到resolve/reject触发 --> 确保异步的时序
if (this.PromiseState === PromiseStatus.PENDING) {
this.onFulfilledCallbacks.push(nextTick(execOnFulfilled));
this.onRejectedCallbacks.push(nextTick(execOnRejected));
}
else if (this.PromiseState === PromiseStatus.FULFILLED) {
nextTick(execOnFulfilled)();
}
else {
nextTick(execOnRejected)();
}
});
return newPromise;
}
catch(onRejected) {
return this.then(undefined, onRejected);
}
// 直接返回一个fulfilled状态的promise
static resolve(value) {
return new MyPromise((resolve) => {
resolve(value);
});
}
// 直接返回rejected状态的promise
static reject(reason) {
return new MyPromise((_resolve, reject) => {
reject(reason);
});
}
static all() { }
static race() { }
static allSettled() { }
}
_MyPromise_instances = new WeakSet(), _MyPromise_clearCallbacks = function _MyPromise_clearCallbacks() {
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
};
// const testPromise = new MyPromise<string>((resolve, reject) => {
// setTimeout(() => resolve('1'), 1000);
// });
// testPromise
// .then<2>((s) => {
// console.log('first:', s);
// return 2;
// })
// .then((r) => console.log('second:', r))
// .catch((e) => console.log('err:', e));
// // 循环引用错误
// const circularRefError: MyPromise<any> = testPromise.then<MyPromise<any>>(
// (r) => circularRefError
// );
// circularRefError.catch<any>((e) => console.error('错误handle: ', e));
// // 嵌套Promise返回
// const nestPromise = new MyPromise((resolve, reject) => {
// resolve();
// });
// nestPromise
// .then((val) => {
// return new MyPromise<string>((res) => res('nest promise'));
// })
// .then((nest) => console.log(nest));
// MyPromise.resolve().then(res => MyPromise.reject('xzxldl')).then(null, s => console.log(s))
MyPromise.resolve().then(() => {
console.log(0);
return MyPromise.resolve(4);
}).then((res) => {
console.log(res);
});
MyPromise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
}).then(() => {
console.log(6);
});