这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战(已经更文6天)
前言
Promise
想必大家都不陌生,在工作或者学习中或多或少会使用到。它是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大。之前的异步编程,通过回调函数去处理的话,可能会陷入无尽的回调地狱中 - 一个回调嵌套一个回调,子子孙孙无穷尽也。
举个栗子:
// 假设 $ajax 是一个异步事件,success 为回调函数,下一个请求依赖上一个请求的结果
$ajax({
// ...
success: function () {
$ajax({
// ...
success: function () {
$ajax({
// ...
success: function () {
// ...
},
});
},
});
},
});
创建一个Promise_
我们先创建一个Promise
对象,为了跟真正的Promise区分开来,我们将其命名为Promise_
。大家都知道,创建一个Promise对象需要传入一个函数,这个函数有两个入参:resolve
和reject
。
我们先来写一个类:
class Promise_ {
constructor(callback) {
// 1.判断入参是否为函数
if (!(callback instanceof Function)) {
console.error(`Promise resolver ${callback} is not a function`);
return;
}
}
}
我们再来想一下resolve
和reject
的作用是什么。
Promise
的实现本质上是发布订阅模式的一种体现。在then
、catch
、finally
等方法传入回调,这些回调函数都是订阅者
,会被存在Promise
内部的订阅者列表里。在resolve
或者reject
调用时,循环这些订阅者列表,发布订阅。
那么基于此原理,我们需要在Promise_
内部维护两个订阅者列表,分别存储then
方法传入的两个回调函数。
所以现在我们的Promise_
:
class Promise_ {
status = "pedding"; // 状态:pedding fulfilled rejected
successDep = []; // 成功时的回调函数集合
errorDep = []; // 失败时的回调函数集合
resultVal = undefined; // 执行完毕的值
constructor(callback) {
// 1.判断入参是否为函数
if (!(callback instanceof Function)) {
console.error(`Promise resolver ${callback} is not a function`);
return;
}
}
}
resolve和reject
我们再来实现一下resolve
和reject
两个方法。这两个方法基本的思路就是在调用时,改变Promise对象内部的状态,分别为fulfilled
和rejected
,并且执行then
方法传入的回调函数列表。
class Promise_ {
status = "pedding"; // 状态:pedding fulfilled rejected
successDep = []; // 成功时的回调函数集合
errorDep = []; // 失败时的回调函数集合
resultVal = undefined; // 执行完毕的值
constructor(callback) {
// 1.判断入参是否为函数
if (!(callback instanceof Function)) {
console.error(`Promise resolver ${callback} is not a function`);
return;
}
callback(this.resolve, this.reject);
}
// notify 发布
resolve = (value) => {
// 赋值
this.status = "fulfilled";
this.resultVal = value;
// 发布订阅
this.successDep.forEach((m) => {
m(value);
});
};
reject = (value) => {
// 赋值
this.status = "rejected";
this.resultVal = value;
// 发布订阅
this.errorDep.forEach((m) => {
m(value);
});
};
}
then
发布订阅的方法实现了,我们再来实现订阅的方法。then
方法接受两个参数,这两个参数都要是函数。then
方法的第一个参数是fulfilled
状态的回调函数,第二个参数是rejected
状态的回调函数,它们都是可选的。
// depend 订阅
then = (success, error) => {
// 判断入参是否都为函数
success = success instanceof Function ? success : () => {};
error = error instanceof Function ? error : () => {};
// 判断状态
if (this.status === "fulfilled") {
success(this.resultVal);
return this;
}
if (this.status === "rejected") {
error(this.resultVal);
return this;
}
// 添加订阅者
this.successDep.push(success);
this.errorDep.push(error);
// 链式调用
return this;
};
现在一个基本的Promise_
类就已经实现了,我们可以来验证一下好使不:
const p = new Promise_((res, rej) => {
setTimeout(() => {
rej(233);
}, 300);
});
p.then(
(val) => {
console.log("resolve:", val);
},
(err) => {
console.log("reject:", err);
}
);
打印:
catch
catch
方法是then
方法的别名,用于指定发生错误时的回调函数。如果异步操作抛出错误,状态就会变为rejected
,就会调用catch
方法指定的回调函数,处理这个错误。另外,then
方法指定的回调函数,如果运行中抛出错误,也会被catch
方法捕获。
实现:
class Promise_ {
status = "pedding"; // 状态:pedding fulfilled rejected
successDep = []; // 成功时的回调函数集合
errorDep = []; // 失败时的回调函数集合
resultVal = undefined; // 执行完毕的值
constructor(callback) {
// 1.判断入参是否为函数
if (!(callback instanceof Function)) {
console.error(`Promise resolver ${callback} is not a function`);
return;
}
try {
callback(this.resolve, this.reject);
} catch (error) {
this.reject(error);
}
}
// notify 发布
resolve = (value) => {
// 赋值
this.status = "fulfilled";
this.resultVal = value;
// 发布订阅
this.successDep.forEach((m) => {
try {
m(value);
} catch (error) {
this.reject(error);
}
});
};
reject = (value) => {
// 赋值
this.status = "rejected";
this.resultVal = value;
// 发布订阅
this.errorDep.forEach((m) => {
m(value);
});
};
// depend 订阅
then = (success, error) => {
// 判断入参是否都为函数
success = success instanceof Function ? success : () => {};
error = error instanceof Function ? error : () => {};
// 判断状态
if (this.status === "fulfilled") {
success(this.resultVal);
return this;
}
if (this.status === "rejected") {
error(this.resultVal);
return this;
}
// 添加订阅者
this.successDep.push(success);
this.errorDep.push(error);
// 链式调用
return this;
};
catch = (error) => {
// 判断入参是否都为函数
error = error instanceof Function ? error : () => {};
if (this.status === "rejected") {
error(this.resultVal);
return this;
}
// 添加订阅者
this.errorDep.push(error);
// 链式调用
return this;
};
}
finally
finally
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。不管promise
最后的状态,在执行完then
或catch
指定的回调函数以后,都会执行finally
方法指定的回调函数。
实现:
finally = (cb) => {
return this.then(
() => cb(),
() => cb()
);
};
Promise_.all
Promise.all
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例,Promise.all
方法接受一个数组作为参数。
const p = Promise.all([p1, p2, p3]);
- 当传入的数组 Promise 成员的状态都变成
fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。 - 当传入的数组 Promise 成员之中有一个被
rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
实现:
// all方法
static all = (arr) => {
// 判断入参是否为一个数组
if (!(arr instanceof Array)) {
console.error(`Promise resolver ${arr} is not a Array`);
return;
}
// 判断是否为空数组
if (arr.length === 0) return new Promise_((res) => res([]));
return new Promise_((res, rej) => {
let resArr = [];
let num = 0;
for (let i = 0; i < arr.length; i++) {
let p = null;
// 检测数组成员是否为Promise_对象
if (!(arr[i] instanceof Promise_)) {
p = Promise_.resolve(arr[i]);
} else {
p = arr[i];
}
p.then((val) => {
resArr[i] = val;
if (++num === arr.length) {
res(resArr);
}
}).catch((err) => {
rej([err]);
});
}
});
};
// 转换其他类型为Promise_类型
static resolve = (val) => {
return new Promise_((res, rej) => {
res(val);
});
};
Promise_.race
Promise.race
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数。
实现:
// race方法
static race = (arr) => {
// 判断入参是否为一个数组
if (!(arr instanceof Array)) {
console.error(`Promise resolver ${arr} is not a Array`);
return;
}
// 判断是否为空数组
if (arr.length === 0) return new Promise_((res) => res([]));
return new Promise_((res, rej) => {
let f = false;
for (let i = 0; i < arr.length; i++) {
let p = null;
// 检测数组成员是否为Promise_对象
if (!(arr[i] instanceof Promise_)) {
p = Promise_.resolve(arr[i]);
} else {
p = arr[i];
}
p.then((val) => {
if (f) return;
f = true;
res(val);
}).catch((err) => {
if (f) return;
f = true;
rej(err);
});
}
});
};
Promise_.any
ES2021 引入了Promise.any
方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。
只要参数实例有一个变成fulfilled
状态,包装实例就会变成fulfilled
状态;如果所有参数实例都变成rejected
状态,包装实例就会变成rejected
状态。
实现:
// any方法
static any = (arr) => {
// 判断入参是否为一个数组
if (!(arr instanceof Array)) {
console.error(`Promise resolver ${arr} is not a Array`);
return;
}
// 判断是否为空数组
if (arr.length === 0) return new Promise_((res) => res([]));
return new Promise_((res, rej) => {
let num = 0;
let f = false;
for (let i = 0; i < arr.length; i++) {
let p = null;
// 检测数组成员是否为Promise_对象
if (!(arr[i] instanceof Promise_)) {
p = Promise_.resolve(arr[i]);
} else {
p = arr[i];
}
p.then((val) => {
if (f) return;
f = true;
res(val);
}).catch((err) => {
if (f) return;
if (++num === arr.length) {
rej(new Error("any error"));
}
});
}
});
};
完整实例
现在,基本完成了一个Promise
类,现在贴出完整版的代码:
class Promise_ {
status = "pedding"; // 状态:pedding fulfilled rejected
successDep = []; // 成功时的回调函数集合
errorDep = []; // 失败时的回调函数集合
resultVal = undefined; // 执行完毕的值
constructor(callback) {
// 1.判断入参是否为函数
if (!(callback instanceof Function)) {
console.error(`Promise resolver ${callback} is not a function`);
return;
}
try {
callback(this.resolve, this.reject);
} catch (error) {
this.reject(error);
}
}
// all方法
static all = (arr) => {
// 判断入参是否为一个数组
if (!(arr instanceof Array)) {
console.error(`Promise resolver ${arr} is not a Array`);
return;
}
// 判断是否为空数组
if (arr.length === 0) return new Promise_((res) => res([]));
return new Promise_((res, rej) => {
let resArr = [];
let num = 0;
for (let i = 0; i < arr.length; i++) {
let p = null;
// 检测数组成员是否为Promise_对象
if (!(arr[i] instanceof Promise_)) {
p = Promise_.resolve(arr[i]);
} else {
p = arr[i];
}
p.then((val) => {
resArr[i] = val;
if (++num === arr.length) {
res(resArr);
}
}).catch((err) => {
rej([err]);
});
}
});
};
// race方法
static race = (arr) => {
// 判断入参是否为一个数组
if (!(arr instanceof Array)) {
console.error(`Promise resolver ${arr} is not a Array`);
return;
}
// 判断是否为空数组
if (arr.length === 0) return new Promise_((res) => res([]));
return new Promise_((res, rej) => {
let f = false;
for (let i = 0; i < arr.length; i++) {
let p = null;
// 检测数组成员是否为Promise_对象
if (!(arr[i] instanceof Promise_)) {
p = Promise_.resolve(arr[i]);
} else {
p = arr[i];
}
p.then((val) => {
if (f) return;
f = true;
res(val);
}).catch((err) => {
if (f) return;
f = true;
rej(err);
});
}
});
};
// any方法
static any = (arr) => {
// 判断入参是否为一个数组
if (!(arr instanceof Array)) {
console.error(`Promise resolver ${arr} is not a Array`);
return;
}
// 判断是否为空数组
if (arr.length === 0) return new Promise_((res) => res([]));
return new Promise_((res, rej) => {
let num = 0;
let f = false;
for (let i = 0; i < arr.length; i++) {
let p = null;
// 检测数组成员是否为Promise_对象
if (!(arr[i] instanceof Promise_)) {
p = Promise_.resolve(arr[i]);
} else {
p = arr[i];
}
p.then((val) => {
if (f) return;
f = true;
res(val);
}).catch((err) => {
if (f) return;
if (++num === arr.length) {
rej(new Error("any error"));
}
});
}
});
};
// 转换其他类型为Promise_类型
static resolve = (val) => {
return new Promise_((res, rej) => {
res(val);
});
};
// notify 发布
resolve = (value) => {
// 赋值
this.status = "fulfilled";
this.resultVal = value;
// 发布订阅
this.successDep.forEach((m) => {
try {
m(value);
} catch (error) {
this.reject(error);
}
});
};
reject = (value) => {
// 赋值
this.status = "rejected";
this.resultVal = value;
// 发布订阅
this.errorDep.forEach((m) => {
m(value);
});
};
// depend 订阅
then = (success, error) => {
// 判断入参是否都为函数
success = success instanceof Function ? success : () => {};
error = error instanceof Function ? error : () => {};
// 判断状态
if (this.status === "fulfilled") {
success(this.resultVal);
return this;
}
if (this.status === "rejected") {
error(this.resultVal);
return this;
}
// 添加订阅者
this.successDep.push(success);
this.errorDep.push(error);
// 链式调用
return this;
};
catch = (error) => {
// 判断入参是否都为函数
error = error instanceof Function ? error : () => {};
if (this.status === "rejected") {
error(this.resultVal);
return this;
}
// 添加订阅者
this.errorDep.push(error);
// 链式调用
return this;
};
finally = (cb) => {
return this.then(
() => cb(),
() => cb()
);
};
}
结语
Promise
对于前端开发来说是非常重要的,在平时工作中或者面试的时候一定会用到。如何深入理解Promise
呢?那一定是自己手写实现一个Promise
。本文实现了一个简易版的Promise
,基本上该有的都有了。
如果看完这篇文章,对您有所帮助的话,还烦请您点个赞,点点关注,祝您生活愉快。