-
Notifications
You must be signed in to change notification settings - Fork 0
/
手写Promise.html
396 lines (349 loc) · 15.7 KB
/
手写Promise.html
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
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Promise TEST</title>
<style>
body {
padding: 16px 32px;
}
pre {
padding: 16px;
line-height: 2;
background-color: #f5f5f5;
}
</style>
</head>
<body>
<H3></H3>
<pre>
Promise/A+ 规范
* 1. new Promise时,需要传递一个 executor 执行器,执行器立刻执行
* 2. executor 接受两个参数,分别是 resolve 和 reject
* 3. promise 只能从 pending 到 rejected, 或者从 pending 到 fulfilled
* 4. promise 的状态一旦确认,就不会再改变
* 5. promise 都有 then 方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled, 和 promise 失败的回onRejected
* 6. 如果调用 then 时,promise已经成功,则执行 onFulfilled,并将promise的值作为参数传递进去。
* 如果promise已经失败,那么执行 onRejected, 并将 promise 失败的原因作为参数传递进去。
* 如果promise的状态是pending,需要将onFulfilled和onRejected函数存放起来,等待状态确定后,再依次将对应的函数执行(发布订阅)
* 7. then 的参数 onFulfilled 和 onRejected 可以缺省
* 8. promise 可以then多次,promise 的then 方法返回一个 promise
* 9. 如果 then 返回的是一个结果,那么就会把这个结果作为参数,传递给下一个then的成功的回调(onFulfilled)
* 10. 如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个then的失败的回调(onRejected)
* 11. 如果 then 返回的是一个promise,那么需要等这个promise,那么会等这个promise执行完,promise如果成功,就走下一个then的成功,如果失败,就走下一个then的失败
</pre>
<script>
/**
* Promise 实现 遵循promise/A+规范
* Promise/A+规范译文:
* https://malcolmyu.github.io/2015/06/12/Promises-A-Plus/#note-4
*/
// promise 三个状态
const PENDING = "Pending";
const FULFILLED = "Fulfilled";
const REJECTED = "Rejected";
function Promise(excutor) {
this.status = PENDING; // 初始状态
this.value = undefined; // fulfilled状态时 返回的信息
this.reason = undefined; // rejected状态时 拒绝的原因
this.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数
this.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数
let self = this; // 缓存当前promise实例对象
function resolve(value) {
// 只能由pending状态 => fulfilled状态 (避免调用多次resolve reject)
if (self.status === PENDING) {
self.status = FULFILLED;
self.value = value;
self.onFulfilledCallbacks.forEach(cb => cb(self.value));
}
}
function reject(reason) {
// 只能由pending状态 => rejected状态 (避免调用多次resolve reject)
if (self.status === PENDING) {
self.status = REJECTED;
self.reason = reason;
self.onRejectedCallbacks.forEach(cb => cb(self.reason));
}
}
try {
excutor(resolve, reject);
} catch (e) {
reject(e);
}
}
/**
* resolve中的值几种情况:
* 1.普通值
* 2.promise对象
* 3.thenable对象/函数
*/
/**
* 对resolve 进行改造增强 针对resolve中不同值情况 进行处理
* @param {promise} promise2 promise1.then方法返回的新的promise对象
* @param {[type]} x promise1中onFulfilled的返回值
* @param {[type]} resolve promise2的resolve方法
* @param {[type]} reject promise2的reject方法
*/
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) { // 如果从onFulfilled中返回的x 就是promise2 就会导致循环引用报错
return reject(new TypeError('循环引用'));
}
// 判断 x 是否是 promise 对象
if (x != null && (typeof x === "object" || typeof x === "function")) {
let called = false; // 避免多次调用
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, y => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, reason => {
if (called) return;
called = true;
reject(reason);
})
} else { // 说明是一个普通对象/函数
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
/**
* [注册fulfilled状态/rejected状态对应的回调函数]
* @param {function} onFulfilled fulfilled状态时 执行的函数
* @param {function} onRejected rejected状态时 执行的函数
* @return {function} newPromsie 返回一个新的promise对象
*/
Promise.prototype.then = function (onFulfilled, onRejected) {
let self = this;
let promise2;
// 处理参数默认值 保证参数后续能够继续执行
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => { return value };
onRejected = typeof onRejected === "function" ? onRejected : reason => { throw reason };
// then里面的FULFILLED/REJECTED状态时 为什么要加setTimeout ?
// 原因:
// 其一 2.2.4规范 要确保 onFulfilled 和 onRejected 方法异步执行(且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行) 所以要在resolve里加上setTimeout
// 其二 2.2.6规范 对于一个promise,它的then方法可以调用多次.(当在其他程序中多次调用同一个promise的then时 由于之前状态已经为FULFILLED/REJECTED状态,则会走的下面逻辑),所以要确保为FULFILLED/REJECTED状态后 也要异步执行onFulfilled/onRejected
// 其二 2.2.6规范 也是resolve函数里加setTimeout的原因
// 总之都是 让then方法异步执行 也就是确保onFulfilled/onRejected异步执行
// 如下面这种情景 多次调用p1.then
// p1.then((value) => { // 此时p1.status 由pending状态 => fulfilled状态
// console.log(value); // resolve
// // console.log(p1.status); // fulfilled
// p1.then(value => { // 再次p1.then 这时已经为fulfilled状态 走的是fulfilled状态判断里的逻辑 所以我们也要确保判断里面onFuilled异步执行
// console.log(value); // 'resolve'
// });
// console.log('当前执行栈中同步代码');
// })
// console.log('全局执行栈中同步代码');
//
if (self.status === FULFILLED) { // 成功态
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject); // 新的promise resolve 上一个onFulfilled的返回值
} catch (e) {
reject(e); // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);
}
});
})
return promise2
}
if (self.status === REJECTED) { // 失败态
promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
return promise2
}
if (self.status === PENDING) {
// 当异步调用resolve/rejected时 将onFulfilled/onRejected收集暂存到集合中
// 为什么加setTimeout?
// 2.2.4规范 onFulfilled 和 onRejected 只允许在 execution context 栈仅包含平台代码时运行.
// 注1 这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。
promise2 = new Promise((resolve, reject) => {
self.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
try {
let x = onFulfilled(value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
});
self.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
try {
let x = onRejected(reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
})
})
});
return promise2
}
};
Promise.resolve = function (value) {
if (value instanceof Promise) {
return value;
}
return new Promise((resolve, reject) => {
if (value && value.then && typeof value.then === 'function') {
setTimeout(() => {
value.then(resolve, reject);
});
} else {
resolve(value);
}
});
};
Promise.reject = function (reason) {
return new Promise((resolve, reject) => reject(reason));
};
Promise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
};
Promise.prototype.finally = function (callback) {
return this.then(
value => {
return Promise.resolve(callback()).then(() => {
return value;
});
},
err => {
return Promise.resolve(callback()).then(() => {
throw err;
});
},
);
};
/**
* Promise.all Promise进行并行处理
* 参数: promise对象组成的数组作为参数
* 返回值: 返回一个Promise实例
* 当这个数组里的所有promise对象全部变为resolve状态的时候,才会resolve。
*/
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let count = 0;
let result = [];
if (promises.length === 0) {
resolve(result);
} else {
for (let i = 0; i < promises.length; i++) {
// promises[i] 可能是普通值
Promise.resolve(promises[i]).then((data) => {
result[i] = data;
if (++count === promises.length) {
resolve(result);
}
}, reject);
}
}
});
}
/**
* Promise.race
* 参数: 接收 promise对象组成的数组作为参数
* 返回值: 返回一个Promise实例
* 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理(取决于哪一个更快)
*/
Promise.race = function (promises) {
if (promises.length === 0) {
return;
}
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
Promise.resolve(promise).then(resolve, reject);
});
});
}
console.log('======== test promise.then ========')
var promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('====promise====')
}, 500);
}).then((res) => {
console.log(res)
})
console.log('======== test Promise.resolve ========')
var p = Promise.resolve(20);
p.then((data) => {
console.log(data);
});
var p2 = Promise.resolve({
then: function (resolve, reject) {
resolve(30);
}
});
p2.then((data) => {
console.log(data)
});
var p3 = Promise.resolve(new Promise((resolve, reject) => {
resolve(400)
}));
p3.then((data) => {
console.log(data)
});
console.log('======== test Promise.all ========')
var promise1 = new Promise((resolve, reject) => {
resolve(3);
})
var promise2 = 42;
var promise3 = new Promise(function (resolve, reject) {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then(function (values) {
console.log(values); //[3, 42, 'foo']
}, (err) => {
console.log(err)
});
var p = Promise.all([]); // will be immediately resolved
var p2 = Promise.all([1337, "hi"]); // non-promise values will be ignored, but the evaluation will be done asynchronously
console.log(p);
console.log(p2)
setTimeout(function () {
console.log('the stack is now empty');
console.log(p2);
});
console.log('======== test Promise.race ========')
Promise.race([
new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }),
undefined,
new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) })
]).then((data) => {
console.log('success ', data);
}, (err) => {
console.log('err ', err);
});
Promise.race([
new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }),
new Promise((resolve, reject) => { setTimeout(() => { resolve(200) }, 200) }),
new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 1000) })
]).then((data) => {
console.log(data);
}, (err) => {
console.log(err);
});
</script>
</body>
</html>