上篇文章学习了 关于 Promise
ES6学习笔记-Promise ; 那么这篇文章就来看看如何自己实现 一个Promise
和它的原型方法和类方法的
本文手写Promise
的主要分为下面这三部分:
- 实现
Promise
大体框架
- 添加异步处理和多次调用
then
方法
- 添加
Promise
的链式调用
- 实现
Promise
的原型方法
- 实现
Promise
的类方法
实现大体框架
我们先来看一个例子:
1 2 3 4 5 6 7 8 9 10
| const p = new Promise((resolve, reject) => { console.log("1"); resolve("2") }) p.then((value) => { console.log("成功:", value) }, (reason) => { console.log("失败:", reason) }); console.log("3")
|
这个例子的输出结果如下:
那么根据这个例子我们可以得到如下的规则:
Promise
是构造函数,new
出来的实例有then
方法。
new Promise
的时候短笛一个参数,这个参数是一个函数(我们一般称为执行器函数–executor
)并且执行器会立即调用。就是上面的例子中 1 会被最先打印出来。
executor
函数有两个参数:resolve
和 reject
;这两个参数也是函数。
new Promise
后的实例是具有状态,默认的状态是 pending
。当置值器resolve
调用后,实例的状态变为fulfilled
。当置值器reject
调用后,实例的状态变为rejected
。
Promise
状态的实例一经改变,就不能再次修改。
- 每一个
Promise
实例都有then
方法,then
方法中有两个参数,这两个参数都是函数。第一个函数叫做成功回调,第二个参数叫做失败回调。当置值器resolve
调用后,then
中的第一个参数函数会被执行;当置值器reject
调用后,then
中的第二个参数函数会被执行。
那我们根据这个规则来写一个大体的Promise
:
构造函数的参数(executor)会立即执行
1 2 3 4
| function MyPromise(executor) { executor(resolve, reject) }
|
new 出来的实例具有then方法
then
方法有两个参数: 成功回调函数,失败回调函数
1 2 3 4
| MyPromise.prototype.then =function (onFulfilled, onRejected) { }
|
new 出来的实例的状态
new 出来的实例具有默认的状态,默认状态为pending
; 需要使用resolve
置值器或者reject
置值器来修改状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function MyPromise(executor) { const self = this; self.status = "pending";
function resolve(value) { self.status = "fulfilled"; }
function reject(reason) { self.status = "rejected"; }
executor(resolve, reject) }
|
这里为什么会把 this
赋值 给 self
呢?值得思考!!
答案:这是因为普调函数的 this
的指向是在调用的时候确定的,如果还不了解 this
指向的, 可以先看看这篇文章 关于this的指向问题。因为我们的 resolve
和 reject
函数里面的this
指向的是Window
。 而不是我们的实例。 所以我们需要使用闭包的用法,让函数能够记住并且能够访问当前的词法作用域,即使函数不是在当前的词法作用域中执行的。即我们上线的写法,将实例this
赋值给self
变量,在resolve
和reject
置值器函数中使用 self
,那么当这两个函数不在当前的词法作用域中调用的时候, 也有对当前词法作用域的使用权限。如果还对闭包不了解的可以先看这边文章 JavaScript中的闭包
then中回调函数的触发
当置值器resolve
调用之后,then
中的第一个函数参数会被执行(成功回调),当置值器reject
调用之后,then
中的第二个函数参数会被执行(失败回调)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| MyPromise.prototype.then =function (onFulfilled, onRejected) {
if (this.status === "fulfilled") { onFulfilled(); }
if (this.status === "rejected") { onRejected() } }
|
Promise的状态一经更改就不能再次改变
Promise的状态一经更改就不能再次改变,所以只有在pending状态的时候才能修状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function resolve(value) { if (self.status === "pending") { self.status = "fulfilled"; } }
function reject(reason) { if (self.status === "pending") { self.status = "rejected"; } }
|
置值器被调用之后, 传入的值会传给then中的回调函数
当置值器resolve
调用后,传入的值是成功的值, 我们称为 value
。这个值会传给then
中的第一个参数,当置值器reject
调用后,实例的值是失败原因,称为reason
。这个值会传给then
中的第二个参数函数
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
| function MyPromise(executor) { const self = this; self.status = "pending";
function resolve(value) { if (self.status === "pending") { self.status = "fulfilled"; self.value = value; } }
function reject(reason) { if (self.status === "pending") { self.status = "rejected"; self.value = reason } }
executor(resolve, reject) }
MyPromise.prototype.then =function (onFulfilled, onRejected) {
if (this.status === "fulfilled") { onFulfilled(this.value); }
if (this.status === "rejected") { onRejected(this.value) } }
|
那么到现在位置, 我们大体的结构写完了。
添加异步处理和多次调用then方法
例子参考
我们还是先来看一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const p = new Promise((resolve, reject) => { console.log("1"); setTimeout(() => { resolve("2"); }, 2000) })
p.then((value) => { console.log("成功1", value) }, (reason) => { console.log("失败1", reason) })
p.then((value) => { console.log("成功2", value) }, (reason) => { console.log("失败2", reason) })
console.log("3");
|
打印的结果为:
例子得出的规则
那针对异步这种情况,我们来看上面的例子可得到解决办法:
当创建Promise
的时候, executor
执行器方法中的resolve("2")
被setTimeout
放到了异步队列中了
所以创建出来的实例当前的状态还是默认的状态,并没有发生改变
那么当代码执行到p.then
的时候, 我们并不知道要去执行then
中的第一个参数(成功回调)还是第二个参数(失败会回调)。
当不知道哪个回调函数会被执行的情况下, 就需要吧把这两个回调保存起来,等到等确定调用哪个回调的时候,再拿出来调用。
因为同一个 promise
实例的then
是可以多次调用的(这里不是指链式调用)。所以我们应该使用数组把成功回调和失败回调保存起来。因为then
是可以多次被调用的。
完善异步和多次调用
那么我们现在的MyPromise
应该添加代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function MyPromise(executor) { const self = this;
self.onResolvedCallbacks = [];
self.onRejectedCallbacks = [];
}
MyPromise.prototype.then =function (onFulfilled, onRejected) {
if (this.status === "pending") { this.onResolvedCallbacks.push(onFulfilled); this.onRejectedCallbacks.push(onRejected) } }
|
现在我们的MyPromise
把我们的成功回调或者失败回调都保存起来了。 那我们该什么时候去调用回调呢?那当然是在状态发生变化的时候去调用。实例的状态只会在 resolve
和 reject
置值器函数中改变, 那么我们就可以在这时候把回调拿出来调用。
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
| function resolve(value) { if (self.status === "pending") { self.onResolvedCallbacks.forEach((fn) => { fn(self.value); }) } }
function reject(reason) { if (self.status === "pending") {
self.onRejectedCallbacks.forEach((fn) => { fn(self.value); }) } }
|
最后附上完整代码:
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
| function MyPromise(executor) { const self = this;
self.onResolvedCallbacks = [];
self.onRejectedCallbacks = [];
self.status = "pending";
function resolve(value) { console.log("置值") if (self.status === "pending") { self.status = "fulfilled"; self.value = value;
self.onResolvedCallbacks.forEach((fn) => { fn(); }) } }
function reject(reason) { if (self.status === "pending") { self.status = "rejected"; self.value = reason;
self.onRejectedCallbacks.forEach((fn) => { fn(); }) } }
executor(resolve, reject) }
MyPromise.prototype.then =function (onFulfilled, onRejected) {
if (this.status === "fulfilled") { onFulfilled(this.value); }
if (this.status === "rejected") { onRejected(this.value) }
if (this.status === "pending") { this.onResolvedCallbacks.push(() => { onFulfilled(this.value) }); this.onRejectedCallbacks.push(() => { onRejected(this.value) }) } }
|
现在我们来测试一下;还是上面的例子,只不过构造函数是我们自己实现的MyPromise
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const p = new MyPromise((resolve, reject) => { console.log("1"); setTimeout(() => { resolve("2"); }, 2000) })
p.then((value) => { console.log("成功1", value) }, (reason) => { console.log("失败1", reason) })
p.then((value) => { console.log("成功2", value) }, (reason) => { console.log("失败2", reason) })
console.log("3");
|
结果如下:
结果跟原生的一样, 完美!!!
then方法是异步执行的处理
我们知道 then
方法返回的是一个Promise
。 而Promise
是一个微任务, 微任务是在同步任务执行完之后再执行的。也就是说 then
方法是异步执行的.
ES6的原生Promise对象已经实现了这一点,但是我们自己的代码是同步执行,我们试试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const p = new MyPromise((resolve, reject) => { console.log("1"); resolve("2"); })
p.then((value) => { console.log("成功1", value) }, (reason) => { console.log("失败1", reason) })
p.then((value) => { console.log("成功2", value) }, (reason) => { console.log("失败2", reason) })
console.log("3");
|
我们期望的结果:
但是实际的结果为:
那关于这样情况如何解决呢,我们可以使用setTimeOut
将同步代码变成异步代码。
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
| function MyPromise(executor) { function resolve(value) { self.onResolvedCallbacks.forEach((fn) => { setTimeout(() => { fn(); }, 0) }) } }
function reject(reason) { self.onRejectedCallbacks.forEach((fn) => { setTimeout(() => { fn(); }, 0) }) } }
}
MyPromise.prototype.then =function (onFulfilled, onRejected) {
if (this.status === "fulfilled") { setTimeout(()=> { onFulfilled(this.value); },0) }
if (this.status === "rejected") { setTimeout(()=> { onRejected(this.value) },0) } }
|
这样即可, 再试试我们的例子返回的结果是我们期望的结果。
支持链式调用
then 链的知识点
我们知道得到一个Promise
对象可以有三种方式:
- 使用
new Promise
来创建
- 使用
Promise
的类方法如:Promise.all()
,Promise.race()
,Promise.allsettled()
,Promise.any()
,Promise.resolve()
,Promise.reject()
等
- 使用
Promise
原型方法 Promise.prototype.xxx(): 如:promise.then(),
promise.catch(),
promise.fanally();
由此我们知道:
每个then
方法返回的是一个新的Promise
对象(第三种创建方式)
为什么是一个新的Promise
呢?因为Promise
实例的状态一旦修改,则不能再修改,所以要返回全新的Promise
既然返回的是一个新的Promise
。 那这个新的Promise
的置值逻辑是什么样子的呢?那我们先来看then
链上的置值逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| try { if (isRejected(p)) { x2 = isValidHandler(rejected) ? rejected(result) :throw result; } else { x2 = isValidHandler(resolve) : resolved(result) : result; } resolve(x2); } catch (error) { reject(error) }
|
由此我们知道比较重要的一点:
- 如果
rejected
和 resolve
是有效的。无论他们返回何值,都将作为resolve
置值器的值直接绑定给这个新的Promise
。如果产生了reject
值。那么就作为rejecte
置值器的值直接绑定给这个新的Promise
。
then链中产生Promise
值的方法有两种:
- 通过抛出异常来是的
Javascript
引擎获得异常对象。
- 通过显示的调用
Promise.reject()
。
现在我们知道新的Promise
的值是重要的一点是由成功回调或者失败回调的返回值来确定的。那成功回调或者失败回调的返回值的类型是什么?如果返回值是一个普调值,该是什么样? 如果返回值不是一个普调值,那又该怎么样?
我们来试验一下:
返回普调值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const p1 = new Promise((resolve, reject) => { resolve("1") }) const p2 = p1.then((value) => { console.log("成功回调:", value); return "2"; }, (reason) => { console.log("失败回调:", reason) })
p2.then((value) => { console.log("p2的值:", value); })
|
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const p1 = new Promise((resolve, reject) => { reject("1") }) const p2 = p1.then((value) => { console.log("成功回调:", value); return "2"; }, (reason) => { console.log("失败回调:", reason); return "3"; })
p2.then((value) => { console.log("p2的值:", value); })
|
结果:
如果返回了一个普调值,那么这个值被resove
置值器直接绑定给下一个Promise
(即 p2
)。意思就是会调用下一个then
的第一个参数
这里注意:如果回调里面没有显示的返回值, 那么函数默认是会返回 undefined
。如:
1 2 3 4 5 6 7 8 9 10 11 12
| const p1 = new Promise((resolve, reject) => { reject("1") }) const p2 = p1.then((value) => { console.log("成功回调:", value); }, (reason) => { console.log("失败回调:", reason); })
p2.then((value) => { console.log("p2的值:", value); })
|
结果:
返回非普调值—Promise
例子1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const p1 = new Promise((resolve, reject) => { resolve("1") }) const p2 = p1.then((value) => { console.log("成功回调:", value); return new Promise((resolve, reject) => { resolve("2"); }) }, (reason) => { console.log("失败回调:", reason); })
p2.then((value) => { console.log("p2的值:", value); })
|
结果:
例子2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const p1 = new Promise((resolve, reject) => { reject("1") }) const p2 = p1.then((value) => { console.log("成功回调:", value); }, (reason) => { console.log("失败回调:", reason); return new Promise((resolve, reject) => { reject("2"); }) })
p2.then((value) => { console.log("p2的值:", value); }, (reason) => { console.log("p2的reason:", reason) })
|
结果:
两个例子可得出:
如果回调函数返回一个Promise
. 那么会根据这个Promise
返回成功还是失败来决定使用resolve
置值器还是 reject
置值器(即调用下一个Promise
的第一个参数还是第二个参数。如果是成功的)。
Promise
返回成功,则调用resolve
置值器,调用下一个then
的第一个参数;如果返回失败,则调用 reject
置值器,调用下一个then
的第二个参数
非普调值—报错异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const p1 = new Promise((resolve, reject) => { resolve("1") }) const p2 = p1.then((value) => { console.log("成功回调:", value); throw new Error("报错了") }, (reason) => { console.log("失败回调:", reason); })
p2.then((value) => { console.log("p2的值:", value); }, (reason) => { console.log("p2的reason", reason) })
|
结果:
1 2
| 成功回调: 1 p2的reason Error: 报错了
|
由此可知:
当报错异常(即上面所说的产生了 reject 值)。会采用reject 置值器,直接调用下一个then
的第二个参数
以上的知识点都来自于:《JavaScript语言精髓与编程实战》读书笔记-Promised的核心机制
完善 链式调用
那么基于上面的知识点我们来完善我们的 MyPromise
:
then 返回的是全新的 Promise
我们可以使用 MyPromise
的executor
函数包裹了一下。而executor
函数是立即执行的(在new 实例的时候就会运行)。所以加上这个包裹是不会对原有的逻辑存在影响。又实现了 只要有then方法执行就会返回Promise
实例。并且是全新的MyPromise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| MyPromise.prototype.then = function (onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => { if (this.status === "fulfilled") { }
if (this.status === "rejected") { }
if (this.status === "pending") { } }) }
|
then 链上返回值的处理
由上面的知识点我们可知上一个Promise
的回调函数的返回值会有三种:
- 返回普调值: 会被当前的
Promise
的resolve
置值器直接绑定给下一个 Promise
。则调用下一个 then
的第一个参数
- 返回
Promise
:会根据当前的Promise
的返回值来决定来使用哪个置置器。当返回 成功值,使用resolve
置值器,调用下一个then
的第一个参数。返回失败值。使用reject
置值器;调用下一个then
的第二个参数。
- 报错异常:如果当发现异常报错了,使用
reject
置值器, 调用下一个then的第二个参数。
对普调值和报错异常的处理
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
| MyPromise.prototype.then =function (onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => { if (this.status === "fulfilled") { setTimeout(() => { try { const result = onFulfilled(this.value); resolve(result); } catch (error) { reject (error) } }, 0) }
if (this.status === "rejected") { setTimeout(() => { try { const result = onRejected(this.value) resolve(result); } catch (error) { reject(error) } }, 0)
}
if (this.status === "pending") { this.onResolvedCallbacks.push(() => { try { const result = onFulfilled(this.value); resolve(result); } catch (error) { reject(error); } }); this.onRejectedCallbacks.push(() => { try { const result = onRejected(this.value); resolve(result); } catch (error) { reject(error); } }) } }) }
|
对返回值是Promise的处理
因为回调函数的返回值有可能是Promise
。 那么针对这种情况, 我们把处理返回值单独提出来处理。
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
| MyPromise.prototype.then = function (onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => { if (this.status === "fulfilled") { setTimeout(() => { try { const result = onFulfilled(this.value);
resolvePromiseResult(result, resolve, reject);
} catch (error) { } }, 0) }
if (this.status === "rejected") { setTimeout(() => { try { const result = onRejected(this.value)
resolvePromiseResult(result, resolve, reject);
} catch (error) { } }, 0)
}
console.log("this值:", this) if (this.status === "pending") { this.onResolvedCallbacks.push(() => { try { const result = onFulfilled(this.value);
resolvePromiseResult(result, resolve, reject);
} catch (error) { } }); this.onRejectedCallbacks.push(() => { try { const result = onRejected(this.value);
resolvePromiseResult(result, resolve, reject);
} catch (error) { } }) } }) }
|
有一种情况, Promise
可能调用了自己本身, 这种是不允许的, 会导致死循环。所以我们resolvePromiseResult
函数应该再加上一个参数,就是自身的Promise
。这样在resolvePromiseResult
函数中判断就好判断了。
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
| MyPromise.prototype.then =function (onFulfilled, onRejected) {
const newMyPromise = new MyPromise((resolve, reject) => { if (this.status === "fulfilled") { try { const result = onFulfilled(this.value); resolvePromiseResult(newMyPromise, result, resolve, reject); } catch (error) { } }
if (this.status === "rejected") { try { const result = onRejected(this.value)
resolvePromiseResult(newMyPromise, result, resolve, reject);
} catch (error) { }
}
if (this.status === "pending") { this.onResolvedCallbacks.push(() => { try { const result = onFulfilled(this.value);
resolvePromiseResult(newMyPromise, result, resolve, reject);
} catch (error) { } }); this.onRejectedCallbacks.push(() => { try { const result = onRejected(this.value);
resolvePromiseResult(newMyPromise, result, resolve, reject);
} catch (error) { } }) } }) return newMyPromise; }
|
下面我们就来看看 resolvePromiseResult
函数的处理:
如果返回的是自己的 Promise 对象的话,那么状态永远pending
状态. 再也无法称为 fulfilled
或者 rejected
。程序是会死掉的, 所有首先处理它:
1 2 3 4 5 6 7
| function resolvePromiseResult(promise, backResult, resolve, reject) { if (promise === backResult) { reject(new Error("Promise发生了循环引用")) } }
|
接下来就是分各种情况判断: 如果 backResult
是一个普调值, 那么直接调用 resolve
置值器置值。如果是一个 Promise
, 对象(then 对象)那么就执行它。
1 2 3 4 5 6 7 8 9 10 11 12
| function resolvePromiseResult(promise, backResult, resolve, reject) { if (promise === backResult) { reject(new Error("Promise发生了循环引用")) }; if (backResult !== null && (typeof backResult === "object" || typeof backResult === "function")) { } else { resolve(backResult); } }
|
如果 backResult
是一个对象 或者 函数的话,那么尝试把then
方法取出来,此时如果报错,那么将是使用reject
置值器置值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function resolvePromiseResult(promise, backResult, resolve, reject) { if (promise === backResult) { reject(new Error("Promise发生了循环引用")) }; if (backResult !== null && (typeof backResult === "object" || typeof backResult === "function")) { try { const then = backResult.then;
} catch (error) {
reject(error); } } else { resolve(backResult); } }
|
如果对象中有then
,并且是函数类型, 那么就可认为是一个 Promise
对象。之后直接调用这个 then
方法。成功回调的话使用 resolve
置值器,失败回调的话使用 reject
置值器
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
| function resolvePromiseResult(promise, backResult, resolve, reject) { if (promise === backResult) { reject(new Error("Promise发生了循环引用")) }; if (backResult !== null && (typeof backResult === "object" || typeof backResult === "function")) {
try {
const then = backResult.then; if (typeof then === "function") { then((y) => {
resolve(y)
}, (error) => { reject(error);
}) }
} catch (error) {
reject(error); } } else { resolve(backResult); } }
|
在执行的时候,我们需要绑定一下 this
值。 因为内部可能用到了this
呀
1 2 3 4 5 6 7 8 9
| then.call(promise, () => { resolve(y) }, (error) => { reject(error); }) }
|
到现在链式调用已经基本完成了,但是还有一种极端的情况。如果 Promise
对象转为成功状态或者失败的状态的时候传入的是一个Promise
对象。那么此时应该继续执行 ,知道最后的 Promise
执行完成。那么久需要递归了。
1 2 3 4 5 6 7 8
| then.call(promise, () => { resolvePromiseResult(promise, y, resolve, reject) }, (error) => { reject(error); })
|
那么现在的 链式调用就全部完成了。
最后我们贴上全部的代码:
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
| function MyPromise(executor) { const self = this;
self.onResolvedCallbacks = [];
self.onRejectedCallbacks = [];
self.status = "pending";
function resolve(value) { if (self.status === "pending") { self.status = "fulfilled"; self.value = value;
self.onResolvedCallbacks.forEach((fn) => { setTimeout(() => { fn(); }, 0) }) } }
function reject(reason) { if (self.status === "pending") { self.status = "rejected"; self.value = reason;
self.onRejectedCallbacks.forEach((fn) => { setTimeout(() => { fn(); }, 0) }) } }
executor(resolve, reject) }
function resolvePromiseResult(promise, backResult, resolve, reject) { if (promise === backResult) { reject(new Error("Promise发生了循环引用")) }; if (backResult !== null && (typeof backResult === "object" || typeof backResult === "function")) {
try {
const then = backResult.then; if (typeof then === "function") { then.call(promise, () => { resolvePromiseResult(promise, y, resolve, reject) }, (error) => { reject(error); }) }
} catch (error) {
reject(error);
} } else { resolve(backResult); } }
MyPromise.prototype.then =function (onFulfilled, onRejected) {
const newMyPromise = new MyPromise((resolve, reject) => { if (this.status === "fulfilled") { setTimeout(() => { try { const result = onFulfilled(this.value); resolvePromiseResult(newMyPromise, result, resolve, reject); } catch (error) { reject (error) } }, 0) }
if (this.status === "rejected") { setTimeout(() => { try { const result = onRejected(this.value) resolvePromiseResult(newMyPromise, result, resolve, reject); } catch (error) { reject(error) } }, 0)
}
if (this.status === "pending") { this.onResolvedCallbacks.push(() => { try { const result = onFulfilled(this.value);
resolvePromiseResult(newMyPromise, result, resolve, reject);
} catch (error) { reject(error); } }); this.onRejectedCallbacks.push(() => { try { const result = onRejected(this.value);
resolvePromiseResult(newMyPromise, result, resolve, reject);
} catch (error) { reject(error); } }) } }) return newMyPromise; }
|
测试例子
最后我们来测试一下:
测试用例1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const p = new MyPromise((resolve, reject) => { console.log("1"); setTimeout(() => { resolve("2"); }) })
p.then((value) => { console.log("成功1", value) }, (reason) => { console.log("失败1", reason) })
p.then((value) => { console.log("成功2", value) }, (reason) => { console.log("失败2", reason) })
console.log("3");
|
实际结果与期望结果一致:
支持异步,支持多次调用then
测试用例2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const p = new MyPromise((resolve, reject) => { console.log("1"); resolve("2"); })
p.then((value) => { console.log("成功1", value) }, (reason) => { console.log("失败1", reason) })
p.then((value) => { console.log("成功2", value) }, (reason) => { console.log("失败2", reason) })
console.log("3");
|
实际结果与期望结果一致:
实现then
方法是异步的,支持多次调用then
测试用例3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const p = new MyPromise((resolve, reject) => { console.log("1"); setTimeout(() => { resolve("2"); }, 1000) }) p.then((value) => { console.log("成功1", value); return 3; }, (reason) => { console.log("失败1", reason); return 4 }) .then((value) => { console.log("成功2", value); return 5 },(reason) => { console.log("失败2", reason); return 6 }) console.log(7)
|
实际结果与期望结构一致:
支持链式调用
Promise 的原型方法
原型除了 then
方法之外, 当然还有 catch
, finally
方法。 那我们来一一实现下。
Promise.prototype.catch()
catch
方法是then
方法的语法糖。 只接受rejected 状态的数据。它就相当于then
的第二个参数函数。 那写的时候直接调用 then 方法的第二个参数函数即可。
1 2 3
| MyPromise.prototype.catch = function(callback) { return this.then(null, callback) }
|
测试:
1 2 3 4 5 6 7
| const p = new MyPromise((resolve, reject) => { reject("失败"); }) p.catch((err) => { console.log(err) })
|
1 2 3 4 5 6 7 8
| const p = new Promise((resolve, reject) => { throw new Error("失败") }) p.catch((err) => { console.log(err) })
|
Promise.prototype.finally()
finally
方法 也是Promise
的一个语法糖,在当前的Promise
执行完then
或者执行完catch
之后均会触发。
注意:finally
的参数(回调函数) 是不带任何的参数的。
1 2 3 4 5 6 7 8 9 10
| MyPromise.prototype.finally = function(callback) { return this.then((value) => { Promise.resolve(callback()) .then(() => value) }, (err) => { Promise.resolve(callback()) .then(() => err) }) }
|
测试:
1 2 3 4 5 6 7 8 9 10 11 12
| const p = new MyPromise((resolve, reject) => { resolve("111") }) p.finally(() => { console.log("222") }) p.then((value) => { console.log("value", value) })
|
Promise 的类方法
Promise.all()
过程
Promise.all(arr)
返回一个新的 Promise
实例,参数是外面传入的多个 Promise
的实例。对于返回新的实例的,有以下这两种情况:
- 如果传入的所有 promise 实例的状态均变为
fulfilled
,那么返回的 promise 实例的状态就是fulfilled
,并且其 value 是 传入的所有 promise 的 value 组成的数组。
- 如果有一个 promise 实例状态变为了
rejected
,那么返回的 promise 实例的状态立即变为rejected
。
代码实现
实现思路:
- 传入的参数不一定是数组对象,可以是”遍历器”
- 传入的每个实例不一定是 promise,需要用
Promise.resolve()
包装
- 借助”计数器”,标记是否所有的实例状态均变为
fulfilled
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| MyPromise.all = function(arg) { const arrPromises = [...arg]; const result = []; let num = 0; return new Promise((resolve,reject) => { arrPromises.forEach((p) => { Promise.resolve((p) => { p.then((vaue) => { result.push(value); num++; if (num === arrPromises.length) { resolve(result); } }) .catch((err) => { reject(err) }) }) }) }) }
|
Promise.race()
过程
尝试resolve 所有元素。只要其任一元素resolved 或 rejected,都将以该结果作为结果promise
Promise.race(iterators)
的传参和返回值与Promise.all
相同。但其返回的 promise 的实例的状态和 value,完全取决于:传入的所有 promise 实例中,最先改变状态那个(不论是fulfilled
还是rejected
)。
实现:
- 某传入实例
pending -> fulfilled
时,其 value 就是Promise.race
返回的 promise 实例的 value
- 某传入实例
pending -> rejected
时,其 error 就是Promise.race
返回的 promise 实例的 error
1 2 3 4 5 6 7 8 9 10 11 12 13
| MyPromise.race = function(arg) { const arrPromises = Array.from(arg); return new Promise((ressolve, reject) => { arrPromises.forEach((p) => { p.then((value) => { resolve(value); }) .catch((err) => { reject(err); }) }); }) }
|
Promise.any
过程
Promise.any(iterators)
的传参和返回值与Promise.all
相同。
如果传入的实例中,有任一实例变为fulfilled
,那么它返回的 promise 实例状态立即变为fulfilled
;如果所有实例均变为rejected
,那么它返回的 promise 实例状态为rejected
。
⚠️Promise.all
与Promise.any
的关系,类似于,Array.prototype.every
和Array.prototype.some
的关系。
实现
实现思路和Promise.all
及其类似。不过由于对异步过程的处理逻辑不同,因此这里的计数器用来标识是否所有的实例均 rejected。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| MyPromise.any = function(arg) { const arrPromises = Array.from(arg); let rejectedNum = 0; let errResult = []; return new Promise((resolve, reject) => { arrPromises.forEach((p) => { p.then((value) => { rresolve(value) }) .catch((err) => { errResult.push(err) rejectedNum++; if (rejectedNum === arrPromises.length) { reject(errResult); } }) }) }) }
|
Promise.allSettled
作用:
作用也是将多个promise实例,包装成一个新的Promise实例.。但是只要这些参数的实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。
过程
Promise.allSettled(iterators)
的传参和返回值与Promise.all
相同。
根据ES2020,此返回的 promise 实例的状态只能是fulfilled
。对于传入的所有 promise 实例,会等待每个 promise 实例结束,并且返回规定的数据格式。
如果传入 a、b 两个 promise 实例:a 变为 rejected,错误是 error1;b 变为 fulfilled,value 是 1。那么Promise.allSettled
返回的 promise 实例的 value 就是:
1 2
| [{ status: "rejected", value: error1 }, { status: "fulfilled", value: 1 }];
|
代码实现
计数器,用于统计所有传入的 promise 实例。
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
| MyPromise.allSetlled = function(arg) { const arrPromises = Array.from(arg); let result = []; let num = 0; return new Promise((resolve, reject) => { arrPromises.forEach((p) => { p.then((value) => { result.push(value); num++; if (num === arrPromises.length) { resolve(result) } }) .catch((err) => { result.push(err); num++; if (num === arrPromises.length) { resolve(result) } }) }) }) }
|
Promise.all、Promise.any 和 Promise.allSettled 中计数器使用对比:
这三个方法均使用了计数器来进行异步流程控制,下面表格横向对比不同方法中计数器的用途,来加强理解:
方法名 |
用途 |
Promise.all |
标记 fulfilled 的实例个数 |
Promise.any |
标记 rejected 的实例个数 |
Promise.allSettled |
标记所有实例(fulfilled 和 rejected)的个数 |
Promise.resolve()
该方法是将现有的对象转成 promise
对象。
过程
实现
1 2 3 4 5 6 7 8
| MyPromise.resolve = function(arg) { if (arg instanceof Promise || (typeof arg === 'object' && 'then' in arg )) return arg; return new Promise((resolve, reject) => { resolve(arg); }) }
|
Promise.reject()
该方法返回一个新的 Promise 实例,该实例的状态为rejected
。
注意:Promise.reject()
方法的参数,会原封不动地作为reject
的理由,变成后续方法的参数。这一点与Promise.resolve
方法不一致。
实现
1 2 3 4 5
| MyPromise.reject = function(arg) { return new Promise((resolve, reject) => { reject(arg); }) }
|
最后
最后附上完整的代码。 当然在写一写Promise的原型和类方法的时候, 可能会用用到了原生的Promise
。当然是可以替换成我们自己的MyPromise
的。
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
| function MyPromise(executor) { const self = this;
self.onResolvedCallbacks = [];
self.onRejectedCallbacks = [];
self.status = "pending";
function resolve(value) { if (self.status === "pending") { self.status = "fulfilled"; self.value = value;
self.onResolvedCallbacks.forEach((fn) => { setTimeout(() => { fn(); }, 0) }) } }
function reject(reason) { if (self.status === "pending") { self.status = "rejected"; self.value = reason;
self.onRejectedCallbacks.forEach((fn) => { setTimeout(() => { fn(); }, 0) }) } }
executor(resolve, reject) }
function resolvePromiseResult(promise, backResult, resolve, reject) { if (promise === backResult) { reject(new Error("Promise发生了循环引用")) }; if (backResult !== null && (typeof backResult === "object" || typeof backResult === "function")) {
try {
const then = backResult.then; if (typeof then === "function") { then.call(promise, () => { resolvePromiseResult(promise, y, resolve, reject) }, (error) => { reject(error); }) }
} catch (error) {
reject(error);
} } else { resolve(backResult); } }
MyPromise.prototype.then = function (onFulfilled, onRejected) {
const newMyPromise = new MyPromise((resolve, reject) => { if (this.status === "fulfilled") { setTimeout(() => { try { const result = onFulfilled(this.value); resolvePromiseResult(newMyPromise, result, resolve, reject); } catch (error) { reject (error) } }, 0) }
if (this.status === "rejected") { setTimeout(() => { try { const result = onRejected(this.value) resolvePromiseResult(newMyPromise, result, resolve, reject); } catch (error) { reject(error) } }, 0)
}
if (this.status === "pending") { this.onResolvedCallbacks.push(() => { try { const result = onFulfilled(this.value);
resolvePromiseResult(newMyPromise, result, resolve, reject);
} catch (error) { reject(error); } }); this.onRejectedCallbacks.push(() => { try { const result = onRejected(this.value);
resolvePromiseResult(newMyPromise, result, resolve, reject);
} catch (error) { reject(error); } }) } }) return newMyPromise; }
MyPromise.prototype.catch = function(callback) { return this.then(null, callback) }
MyPromise.prototype.finally = function(callback) { return this.then((value) => { Promise.resolve(callback()) .then(() => value) }, (err) => { Promise.resolve(callback()) .then(() => err) }) }
MyPromise.all = function(arg) { const arrPromises = Array.from(arg); const result = []; let num = 0; return new Promise((resolve,reject) => { arrPromises.forEach((p) => { Promise.resolve((p) => { p.then((vaue) => { result.push(value); num++; if (num === arrPromises.length) { resolve(result); } }) .catch((err) => { reject(err) }) })
}) }) }
MyPromise.race = function(arg) { const arrPromises = Array.from(arg); return new Promise((ressolve, reject) => { arrPromises.forEach((p) => { p.then((value) => { resolve(value); }) .catch((err) => { reject(err); }) }); }) }
MyPromise.any = function(arg) { const arrPromises = Array.from(arg); let rejectedNum = 0; let errResult = []; return new Promise((resolve, reject) => { arrPromises.forEach((p) => { p.then((value) => { rresolve(value) }) .catch((err) => { errResult.push(err) rejectedNum++; if (rejectedNum === arrPromises.length) { reject(errResult); } }) }) }) }
MyPromise.allSetlled = function(arg) { const arrPromises = Array.from(arg); let result = []; let num = 0; return new Promise((resolve, reject) => { arrPromises.forEach((p) => { p.then((value) => { result.push(value); num++; if (num === arrPromises.length) { resolve(result) } }) .catch((err) => { result.push(err); num++; if (num === arrPromises.length) { resolve(result) } }) }) }) }
const obj = { then: () => { } } console.log('then' in obj)
MyPromise.resolve = function(arg) { if (arg instanceof Promise || (typeof arg === 'object' && 'then' in arg )) return arg; return new Promise((resolve, reject) => { resolve(arg); }) }
MyPromise.reject = function(arg) { return new Promise((resolve, reject) => { reject(arg); }) }
|