前言
Hello~大家好! 新年伊始,先给大家拜个晚年:祝各位掘友们2024年工作顺利!身体健康!万事如意!!!
回到正题,在前端项目开发中,无论你是刚入门的小白还是“叱咤风云”多年的老鸟,一定都绕不开Promise的使用。
本篇文章对于Promise
的介绍和用法就不多赘述了,我们专心来探究Promise
内部实现的逻辑,阅读本篇文章的最终目的是能够实现一个自定义的Promise。
还是老样子,阅读前请确保以下几点:
熟练掌握Promise使用语法
有些同学可能不熟悉ES6中class的用法,所以本篇文章我们使用ES5中的函数和原型链来模拟ES6中的class特性
如果同学们对ES5中的原型对象和原型链知识感兴趣的话可以在评论区告诉我,我会找时间更新一篇《搞懂原型对象和原型链》
文章中涉及到this指向的知识,如果你对this指向有疑问或者不熟悉可以先看看我之前的两篇文章:
Promise 使用方法
let p = new Promise((resolve, reject) => {
resolve("ok");
});
console.log(p);
p.then(
(value) => {
console.log(value);
},
(error) => {
console.log(error);
}
);
我们从Promise
最简单的一个使用方法开始,可以看到上面代码和输出结果:
- 通过
new
关键字创建了一个Promise
类型的实例对象赋值为p
- 给
Promise
构造函数传入一个回调函数 p
实例对象调用了自身的then
方法
一、 搭建框架
在这一步里,我们先搭建好基础框架:
-
既然
p
实例对象是通过new
关键字创造出来的,那么Promise
就是一个构造函数。 -
Promise
构造函数接收一个参数(回调函数),我们叫它执行器函数:executor
。 -
p
实例对象可以调用then
方法,那就说明Promise
的原型对象上有一个then
函数,并且接收两个参数,我们命名为onResolved
和onRejected
。
function MyPromise(executor){
};
MyPromise.prototype.then = function (onResolved,onRejected){
};
好了,第一步就大功告成了,是不是很简单?
后面我们会基于这个搭建出来的“框架”一点一点的去完善实现功能
二、同步调用执行器函数,且需要带上两个参数 reslove/reject
function MyPromise(executor){
+ // resolve 函数
+ function resolve(data){};
+ // reject 函数
+ function reject(data){}
+ //同步调用执行器函数
+ executor(resolve,reject);
}
MyPromise.prototype.then = function (onResolved,onRejected){
};
-
Promise
函数接收到了执行器以后,需要同步调用这个执行器函数executor
,并且传入resolve
和reject
参数 -
那么问题来了,
resolve
和reject
是什么东东呢? -
回到最开始的使用案例代码里面,我们看到
resolve("ok");
reject("faild");
-
resolve
和reject
其实是更改Promise
状态的两个函数,两者同样都是接收一个data
参数
三、 实现resolve函数和reject函数
在上一步里面我们仅仅是定义了resolve
函数和reject
函数,在这一步里我们来实现resolve
函数和reject
函数的逻辑
1. resolve函数
resolve函数的功能是什么呢?
-
修改状态:在用户使用
resolve
函数后,Promise
的状态从初始化的pending
状态变成fulfilled
状态 -
设置结果值:在用户使用
resolve
函数后,Promise
的结果值变成用户传入的数据。
function MyPromise(executor){
+ this.PromiseState = 'pending';
+ this.PromiseResult = null;
// 保存实例对象的this值
+ const self = this;
// resolve 函数
function resolve(data){
+ // 1.修改对象的状态 (promiseState)
+ self.PromiseState = 'fulfilled';
+ // 2.设置对象结果值(promiseResult)
+ self.PromiseResult = data;
};
// reject 函数
function reject(data){ }
//同步调用执行器函数
executor(resolve,reject);
}
- 需要特别注意的一点:这里的this指向要小心处理。为什么呢?我们还是回到使用案例里面看看:
let p = new Promise((resolve, reject) => {
resolve("ok");
});
resolve
函数是处在箭头函数里面的,这个时候调用resolve
函数,this指向
是window
。
我们期望这个this
是指向实例对象p
身上,所以需要提前保存this
指向。
2. reject函数
reject
函数和resolve
函数是同样的逻辑,不同的是状态更改为rejected
function MyPromise(executor){
this.PromiseState = 'pending';
this.PromiseResult = null;
// 保存实例对象的this值
const self = this;
// resolve 函数
function resolve(data){
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'fulfilled';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
};
+ // reject 函数
+ function reject(data){
+ // 1.修改对象的状态 (promiseState)
+ self.PromiseState = 'rejected';
+ // 2.设置对象结果值(promiseResult)
+ self.PromiseResult = data;
+ }
//同步调用执行器函数
executor(resolve,reject);
}
四、 处理抛出异常情况
- 在执行器函数里面,除了通过
resolve
和reject
函数可以改变Promise
的状态。使用throw
关键字抛出异常也可以改变状态。我们先看下面的原生Promise例子效果:
let p = new Promise((resolve, reject) => {
throw "error msg";
});
console.log(p);
p.then(
(value) => {
console.log("res:", value);
},
(error) => {
console.log("error:", error);
}
);
-
可以看到,如果在执行器函数内部中抛出了错误,那么
Promise
的状态就会变成rejected
,且then
函数中就会执行失败的回调函数。问题来了,如何实现这一功能呢? -
其实非常简单,我们使用
try catch
语句包裹执行器函数executor
,当catch
到错误的时候,执行rejec
t函数,将Promise
的状态改为失败状态,将error
值当成Promise
的结果值。
function MyPromise(executor) {
this.PromiseState = 'pending';
this.PromiseResult = null;
// 保存实例对象的this值
const self = this;
// resolve 函数
function resolve(data) {
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'fulfilled';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
};
// reject 函数
function reject(data) {
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'rejected';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
}
// 处理异常
+ try {
+ //同步调用执行器函数
+ executor(resolve, reject);
+ } catch (error) {
+ // 修改promise状态为失败
+ reject(e)
+ }
}
五、 promise状态只能改变一次
目前我们自定义的Promise
有一个非常大的问题,就是Promise
的状态可以多次改变。
let p = new MyPromise((resolve, reject) => {
reject('error');
resolve('ok');
});
console.log(p);
-
可以看到,
Promise
的状态从失败状态又变成了成功状态,这明显是不符合Promise
的特点的。 -
所以我们在
resolve
函数和reject
函数中要加入一个判断限制:-
Promise的状态只能从 pending 变成 fulfilled 状态
-
Promise的状态只能从 pending 变成 rejected 状态
-
function MyPromise(executor) {
this.PromiseState = 'pending';
this.PromiseResult = null;
// 保存实例对象的this值
const self = this;
// resolve 函数
function resolve(data) {
+ if(self.PromiseState !== 'pending') return;
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'fulfilled';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
};
// reject 函数
function reject(data) {
+ if(self.PromiseState !== 'pending') return;
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'rejected';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
}
// 处理异常
try {
//同步调用执行器函数
executor(resolve, reject);
} catch (error) {
// 修改promise状态为失败
reject(e)
}
}
六、 实现then方法功能
1.执行回调函数
then
方法的第一个功能就是根据Promise
状态调用不同的回调函数, 且在onResolved
方法和onRejected
方法中加入参数(PromiseResult)
+ MyPromise.prototype.then = function (onResolved, onRejected) {
+ if(this.PromiseState === 'fulfilled'){
+ onResolved(this.PromiseResult);
+ }
+ if(this.PromiseState === 'rejected'){
+ onRejected(this.PromiseResult);
+ }
+ }
2. 异步任务回调的处理
我们来看一个例子:
let p = new MyPromise((resolve, reject) => {
setTimeout(function () {
resolve("ok");
}, 1000);
});
console.log(p);
p.then(
(value) => {
console.log("res:", value);
},
(error) => {
console.log("error:", error);
}
);
-
可以看到,
Promise
还是pending
状态,并且没有执行任何一个回调函数。这是为什么呢? -
如果是在正常同步代码逻辑执行时,
resolve
或者reject
函数后,状态会改变。接着执行then
方法,then
方法会根据状态,调用不同的回调函数。 -
但是在异步的时候,计时器1秒钟后才会改变状态,在这1秒钟之前,如果执行了then函数,此时状态是pending,then方法不知道调用哪个回调函数。
-
所以我们在
then
方法里面,加入一个pending
状态的判断,如果执行then
函数时,Promise
状态还没改变,就先把这两个回调函数保存起来。 -
等到调用
resolve
函数或者reject
函数时,再取出来根据状态执行不同的回调函数
function MyPromise(executor) {
this.PromiseState = 'pending';
this.PromiseResult = null;
// 保存回调函数
+ this.callback = {};
// 保存实例对象的this值
const self = this;
// resolve 函数
function resolve(data) {
if (self.PromiseState !== 'pending') return;
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'fulfilled';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
// 执行成功的回调函数
+ if (self.callback.onResolved) {
+ self.callback.onResolved(data);
+ }
};
// reject 函数
function reject(data) {
if (self.PromiseState !== 'pending') return;
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'rejected';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
// 执行失败的回调函数
+ if (self.callback.onRejected) {
+ self.callback.onRejected(data);
+ }
}
// 处理异常
try {
//同步调用执行器函数
executor(resolve, reject);
} catch (error) {
// 修改promise状态为失败
reject(e)
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
if (this.PromiseState === 'fulfilled') {
onResolved(this.PromiseResult);
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult);
}
+ if (this.PromiseState === 'pending') {
+ this.callback = {
+ onResolved,
+ onRejected
+ }
+ }
}
七、指定多个回调函数的实现
我们继续看下一个例子,给MyPromise
指定多个then
函数:
let p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("ok");
}, 1000);
});
console.log(p);
p.then((value) => {
console.log("res1:", value);
});
p.then((value) => {
console.log("res2:", value);
});
p.then((value) => {
console.log("res3:", value);
});
p.then((value) => {
console.log("res4:", value);
});
-
executor
中如果是异步操作,这时候指定了多个then
函数,那么在第二个then
函数执行的时候,第二个then
里面的成功回调函数,会覆盖之前的回调函数。 -
同理,第三个
then
里面的成功回调函数会覆盖前面第二个then
指定的回调函数。以此类推。 -
如何解决这个问题呢?非常简单。只需要将原来
callback
对象改为数组即可
function MyPromise(executor) {
this.PromiseState = 'pending';
this.PromiseResult = null;
+ this.callbacks = [];
// 保存实例对象的this值
const self = this;
// resolve 函数
function resolve(data) {
if (self.PromiseState !== 'pending') return;
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'fulfilled';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
// 调用成功的回调函数
+ self.callbacks.forEach(item=>{
+ if (item.onResolved) {
+ item.onResolved(data);
+ }
+ })
};
// reject 函数
function reject(data) {
if (self.PromiseState !== 'pending') return;
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'rejected';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
// 调用成功的失败函数
+ self.callbacks.forEach(item=>{
+ if (item.onRejected) {
+ item.onRejected(data);
+ }
+ })
}
// 处理异常
try {
//同步调用执行器函数
executor(resolve, reject);
} catch (error) {
// 修改promise状态为失败
reject(e)
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
if (this.PromiseState === 'fulfilled') {
onResolved(this.PromiseResult);
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult);
}
+ if (this.PromiseState === 'pending') {
+ this.callbacks.push({
+ onResolved,
+ onRejected
+ })
+ }
}
八、对then方法的返回结果进行处理
-
then
方法是可以有返回值的,且返回值是一个新的Promise
类型的实例对象。 -
那么问题来了,这个新的
Promise
类型的实例对象的结果值是什么呢? -
答案:结果是由
then
函数选择执行哪一个回调函数的结果来决定的。 -
下面我们分为两种情况进行分析:
1. 同步情况
先看下面的例子:
then函数返回值不为Promise类型
let p = new Promise((resolve, reject) => {
resolve("ok");
});
const result = p.then((value) => {
console.log("res:", value);
// 相当于 return undefined
});
console.log(result)
- 可以看到在执行成功的回调函数以后,
return
了一个undefined
,此时result
是一个新的Promise
实例对象,状态为成功且值为undefined
then函数返回值为Promise类型
let p = new Promise((resolve, reject) => {
resolve("ok");
});
const result = p.then((value) => {
console.log("res:", value);
return new Promise((resolve)=>{
resolve("okok")
})
});
- 可以看到在执行成功的回调函数以后,
return
了一个新的Promise
对象,此时result
是一个新的Promise实例对象,状态为成功值且为‘okok’
代码功能完善:
- 首先要修改then方法的返回值: 返回值应该是一个新的
Promise
实例对象: (return new Promise())
- 获取回调函数的执行结果:
let result = onResolved(this.PromiseResult);
- 判断结果类型:
- 如果是
Promise
类型。then方法里面可能有抛出异常,还需要加入try..catch
对异常进行处理 - 如果不是
Promise
类型,直接将状态变为成功的状态。
- 如果是
function MyPromise(executor) {
this.PromiseState = 'pending';
this.PromiseResult = null;
this.callbacks = [];
// 保存实例对象的this值
const self = this;
// resolve 函数
function resolve(data) {
if (self.PromiseState !== 'pending') return;
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'fulfilled';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
// 调用成功的回调函数
self.callbacks.forEach(item => {
if (item.onResolved) {
item.onResolved(data);
}
})
};
// reject 函数
function reject(data) {
if (self.PromiseState !== 'pending') return;
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'rejected';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
// 调用成功的失败函数
self.callbacks.forEach(item => {
if (item.onRejected) {
item.onRejected(data);
}
})
}
// 处理异常
try {
//同步调用执行器函数
executor(resolve, reject);
} catch (error) {
// 修改promise状态为失败
reject(e)
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
return new MyPromise((resolve, reject) => {
if (this.PromiseState === 'fulfilled') {
try {
let result = onResolved(this.PromiseResult);
if (result instanceof MyPromise) {
result.then(res => {
resolve(res);
}, err => {
reject(err)
})
} else {
// 结果的对象状态为成功
resolve(result);
}
} catch (error) {
reject(error)
}
}
if (this.PromiseState === 'rejected') {
let result = onRejected(this.PromiseResult);
}
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved,
onRejected
})
}
})
}
异步情况
异步情况是指在执行器函数中出现了异步代码,请看下面原生Promise
代码:
let p = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve("ok");
}, 1000)
});
const result = p.then((value) => {
console.log("res:", value);
});
console.log(result)
我们替换成自定义的MyPromise你会发现并没有满足我们的期望,此时的状态还是pending
let p = new MyPromise((resolve, reject) => {
setTimeout(()=>{
resolve("ok");
}, 1000)
});
const result = p.then((value) => {
console.log("res:", value);
});
console.log(result)
为了更好的说明原因, 请看下面的图:
- 当计时器执行的时候,此时还没到1秒,
Promise
的状态还是pending
- 那么在执行
then
函数的时候,是不是就进入pending
状态的判断逻辑里面。在这段逻辑里面,代码知识往callbacks
数组里面添加了回调函数,并没有改变状态。 - 此时
then
函数return
了一个新的Promise
对象给result
,那这个result
不就是pending
状态了吗?
完善代码
知道了原因以后,我们继续对代码进行改造:
-
在
then
函数中,如果是pending
状态,在push进callbacks
数组时,同时执行这个回调函数 -
获取到这个回调函数的结果
-
对这个结果进行判断是否是
Promise
对象类型 -
同样的,我们需要注意this指向的问题
function MyPromise(executor) {
this.PromiseState = 'pending';
this.PromiseResult = null;
this.callbacks = [];
// 保存实例对象的this值
const self = this;
// resolve 函数
function resolve(data) {
if (self.PromiseState !== 'pending') return;
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'fulfilled';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
// 调用成功的回调函数
self.callbacks.forEach(item => {
if (item.onResolved) {
item.onResolved(data);
}
})
};
// reject 函数
function reject(data) {
if (self.PromiseState !== 'pending') return;
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'rejected';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
// 调用成功的失败函数
self.callbacks.forEach(item => {
if (item.onRejected) {
item.onRejected(data);
}
})
}
// 处理异常
try {
//同步调用执行器函数
executor(resolve, reject);
} catch (error) {
// 修改promise状态为失败
reject(e)
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
+ const self = this;
return new MyPromise((resolve, reject) => {
if (this.PromiseState === 'fulfilled') {
try {
let result = onResolved(this.PromiseResult);
if (result instanceof MyPromise) {
result.then(res => {
resolve(res);
}, err => {
reject(err)
})
} else {
// 结果的对象状态为成功
resolve(result);
}
} catch (error) {
reject(error)
}
}
if (this.PromiseState === 'rejected') {
let result = onRejected(this.PromiseResult);
}
if (this.PromiseState === 'pending') {
+ this.callbacks.push({
+ onResolved: function () {
+ try {
+ let result = onResolved(self.PromiseResult);
+ if (result instanceof MyPromise) {
+ result.then(res => {
+ resolve(res);
+ }, err => {
+ reject(err)
+ })
+ } else {
+ // 结果的对象状态为成功
+ resolve(result);
+ }
+ } catch (error) {
+ reject(error)
+ }
+
+ },
+ onRejected: function () {
+ try {
+ let result = onRejected(self.PromiseResult);
+ if (result instanceof MyPromise) {
+ result.then(res => {
+ resolve(res);
+ }, err => {
+ reject(err)
+ })
+ } else {
+ // 结果的对象状态为成功
+ resolve(result);
+ }
+ } catch (error) {
+ reject(error)
+ }
+ }
+ })
+ }
+ })
+ }
九、完善then函数rejected状态的处理
-
细心的同学会发现,在then函数里面,我们之前只对
fulfilled
状态进行了处理,对rejected
状态还没完善逻辑。 -
其实
rejected
状态中的逻辑跟fulfilled
状态的处理逻辑是相似的,并且在pending
状态中,充满了各种冗余相似的代码,如下图所示。所以这一节我们将代码进行一个封装和完善。
function MyPromise(executor) {
this.PromiseState = 'pending';
this.PromiseResult = null;
this.callbacks = [];
// 保存实例对象的this值
const self = this;
// resolve 函数
function resolve(data) {
if (self.PromiseState !== 'pending') return;
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'fulfilled';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
// 调用成功的回调函数
self.callbacks.forEach(item => {
if (item.onResolved) {
item.onResolved(data);
}
})
};
// reject 函数
function reject(data) {
if (self.PromiseState !== 'pending') return;
// 1.修改对象的状态 (promiseState)
self.PromiseState = 'rejected';
// 2.设置对象结果值(promiseResult)
self.PromiseResult = data;
// 调用成功的失败函数
self.callbacks.forEach(item => {
if (item.onRejected) {
item.onRejected(data);
}
})
}
// 处理异常
try {
//同步调用执行器函数
executor(resolve, reject);
} catch (error) {
// 修改promise状态为失败
reject(e)
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
const self = this;
return new MyPromise((resolve, reject) => {
// 封装工具函数
+ function callbackUtil(type){
+ try {
+ let result = type(self.PromiseResult);
+ if (result instanceof MyPromise) {
+ result.then(res => {
+ resolve(res);
+ }, err => {
+ reject(err)
+ })
+ } else {
+ // 结果的对象状态为成功
+ resolve(result);
+ }
+ } catch (error) {
+ reject(error)
+ }
+ }
if (this.PromiseState === 'fulfilled') {
+ callbackUtil(onResolved)
}
if (this.PromiseState === 'rejected') {
+ callbackUtil(onRejected)
}
if (this.PromiseState === 'pending') {
this.callbacks.push({
+ onResolved: function () {
+ callbackUtil(onResolved)
},
+ onRejected: function () {
+ callbackUtil(onRejected)
}
})
}
})
}
十、完善catch方法与解决异常穿透问题
catch方法
- catch方法的功能:Promise 对象的 catch() 方法用于注册一个在 promise 被拒绝时调用的函数。
- 官方的定义稍微有点拗口,简单来说就是当
Promise
状态称为rejected
时,这个catch
方法就会执行
继续看一个原生Promise的效果:
let p = new Promise((resolve, reject) => {
setTimeout(()=>{
reject("error");
}, 1000)
});
p.catch(err => {
console.log(err) // error
})
我们继续完善catch
方法:
+ MyPromise.prototype.catch = function (onRejected) {
+ return this.then(undefined, onRejected);
+ };
异常穿透
先看原生Promise
的效果,在then
方法中指定回调函数时,是可以允许用户不传入成功或者失败的回调函数。
let p = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve("ok");
}, 1000)
});
p.then(res => {
console.log(res);
}).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
完善代码
MyPromise.prototype.then = function (onResolved, onRejected) {
const self = this;
//判断回调函数参数
+ if (typeof onRejected !== "function") {
+ onRejected = (reason) => {
+ throw reason;
+ };
+ }
+ if (typeof onResolved !== "function") {
+ onResolved = (value) => value;
+ }
return new MyPromise((resolve, reject) => {
// 封装函数
function callbackUtil(type){
try {
let result = type(self.PromiseResult);
if (result instanceof MyPromise) {
result.then(res => {
resolve(res);
}, err => {
reject(err)
})
} else {
// 结果的对象状态为成功
resolve(result);
}
} catch (error) {
reject(error)
}
}
if (this.PromiseState === 'fulfilled') {
callbackUtil(onResolved)
}
if (this.PromiseState === 'rejected') {
callbackUtil(onRejected)
}
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: function () {
callbackUtil(onResolved)
},
onRejected: function () {
callbackUtil(onRejected)
}
})
}
})
}
十一、实现 Promise.resolve方法
resolve
函数返回的是一个新的Promise
类型的实例对象resolve
函数如果接收的参数是普通数据类型,那么返回的实例对象状态为成功fulfilled
状态resolve
函数如果接收的参数是Promise
类型的实例对象,需要判断是什么状态。- 如果是成功状态,那么
resolve
函数返回的实例对象状态为成功fulfilled
状态 - 如果是失败状态,那么
resolve
函数返回的实例对象状态为成功rejected
状态
MyPromise.resolve = function (value) {
return new MyPromise((resolve, reject) => {
if (value instanceof MyPromise) {
value.then(
(v) => {
resolve(v);
},
(e) => {
reject(e);
}
);
} else {
resolve(value);
}
});
};
十二、实现Promise.reject方法
reject
方法永远都是返回失败的Promise
实例对象,状态为rejected
。
MyPromise.reject = function (value) {
return new MyPromise((resolve, reject) => {
reject(value);
});
};
十三、实现Promise.all方法
- 当传入一个 Promise 对象的数组时,
Promise.all
方法会返回一个新的 Promise。 - 这个新的
Promise
在所有输入的Promise
都成功(resolve)后才会resolve
,并且其结果是一个包含每个输入Promise
结果值的数组,顺序与输入数组相同。 - 如果任何一个输入的
Promise
被拒绝(reject),那么Promise.all
返回的 Promise 会立即 reject,并返回第一个被拒绝的 Promise 的拒绝原因。
MyPromise.all = function (promises) {
return new MyPromise((resolve, reject) => {
// 如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved)状态的 Promise
if (promises.length === 0) return resolve(promises);
let count = 0;
let result = [];
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(v) => {
//只要执行了当前函数,就代表当前promise对象成功了
//条件:必须每个promise对象都成功才能执行resolve
count++;
//将当前promise对象结果存入数组,直接使用push()方法会有小瑕疵,无法固定顺序
result[i] = v;
if (count === promises.length) {
resolve(result);
}
},
(e) => {
reject(e);
}
);
}
});
};
十四、实现 Promise.allsettled 方法
Promise.allSettled
同样接受一个可迭代对象,并返回一个新的 Promise 对象。- 返回的 Promise 对象在所有传入的 Promise 都已经解决(无论成功还是失败)后才会被解决。该 Promise 的解决值是一个数组,包含所有传入的 Promise 的结果,每个结果都是一个对象,包含
status
字段表示状态(”fulfilled” 表示成功,”rejected” 表示失败),以及相应的value
或reason
字段表示值或拒绝原因。
MyPromise.allSettled = function (promises) {
return new MyPromise((resolve, reject) => {
if (promises.length === 0) return resolve(promises);
let count = 0;
let result = [];
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(v) => {
//只要执行了当前函数,就代表当前promise对象成功了
count++;
//将当前promise对象结果存入数组,直接使用push()方法会有小瑕疵,无法固定顺序
result[i] = v;
if (count === promises.length) {
resolve(result);
}
},
(e) => {
count++;
result[i] = e;
if (count === promises.length) {
resolve(result);
}
}
);
}
});
};
十五、实现 Promise.race方法
- 注意: promise数组中如果有某个promise对象是异步的,需要判断哪个promise对象最先执行
MyPromise.race = function (promises) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(v) => {
resolve(v);
},
(e) => {
reject(e);
}
);
}
});
};
十六、完善then方法中的回调函数异步执行情况
什么意思呢?我们用原生的Promise看看效果就清楚了:
let p = new Promise((resolve, reject) => {
resolve('ok');
console.log(111)
});
p.then(res => {
console.log(222);
})
console.log(333)
// 执行顺序 111->333->222
所以我们在执行回调函数时,也需要进行异步处理,很简单,加上setTimeout
包裹一下就能实现
结语
-
好了,没骗你们吧!!!
-
十六个步骤以后,我们就已经实现了基础版 的自定义Promise了,为什么说是基础版呢?因为在许多方法实现上我们没有做一些参数数据的校验,在某些判断语句上也有处理比较粗糙的情况出现。
-
用当前这个代码去跑 Promises/A+ 规范的测试用例当然是无法全部通过的。
-
当然这也无伤大雅,最重要的是能够打消同学们对Promise的恐惧 并且能快速搞懂Promise的核心实现原理
-
如果有追求完美的同学可以继续照着 Promises/A+ 规范和使用ES6中的class 来完善我们的自定义Promise
-
最后谢谢大家的观看,如果有帮助到你~希望能得到你的点赞收藏评论!
-
大家也可以移步我的主页,有其他文章供大家阅读:秋天的一阵风
原文链接:https://juejin.cn/post/7336871908085989427 作者:秋天的一阵风