关于 Promise 的易错理解及四种静态方法 all allSettled race any 的实现方法。原文见此promise静态方法 - Sunburst89757's blog
Promise 的理解
- Promise 总共有三种状态,分别是 Pending Fulfilled Rejected 三种状态,状态流转只能是单向的,只能由 Pending-->Fulfilled 或者 Pending-->Rejected.
- Promise 实例化对象上的 then 只是取出 Promise 的值而已,并不是执行 Promise,Promise 执行的时候是在实例化对象的时候就已经进行了。
- Promise 实例化的对象上 then 方法 return 的值会自动被 Promise 包裹,因此 Promise 会形成链式调用。
- catch 方法实际上是 Promise 实例对象上的第二个参数(错误捕获的回调函数)的语法糖。
- all allSettled race any 都是 Promise 这个类的静态方法只能由
Promise.all这样调用,不是在实例化的对象上调用。
all 方法
- 作用:接收一组值,用数组存储,返回一个 Promise 对象,当 Promise 中没有 reject 的值时,将所有 promise resolve 的值存储到数组里,通过 then 的第一个回调取出。只要出现 reject 就立刻 catch
- 注意事项:
- 接收的值可以是 Promise 也可以不是 Promise,函数会自动将非 Promise 的值转化为 Promise。
- 所有 Promise 都执行了,then 方法只是进行了取值操作
- 实现注意细节
- 必须对入参进行数组校验
- 入参可以不是 Promise 但要对非 Promise 的值进行包裹成为 Promise
- 输出的结果的顺序得和入参的顺序保持一致
- 测试时候建议将 ts 注释删除使用 js 在浏览器内测试
function promiseAll(arr: any[]): Promise<any> {
return new Promise((resolve, reject) => {
// 入参必须保证是数组
if (!Array.isArray(arr)) reject(new Error("传参必须是数组"));
const values: any[] = [];
let counter = 0;
arr.forEach((promise, index) => {
// 传递的参数可以不是Promise 但得经过Promise包装
Promise.resolve(promise)
.then((res) => {
// 数组输出的结果和入参的Promise顺序得一致
values[index] = res;
counter++;
//只有所有的Promise都resolve了之后才返回值
if (counter === arr.length) resolve(values);
})
.catch((err) => {
// 一个Promise出错了reject
reject(err);
});
});
});
}
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1000);
}, 1000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("err:2000");
}, 2000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("前面失败了但是promise依然执行");
resolve(3000);
}, 3000);
});
const p4 = "非Promise值";
promiseAll([p3, p4, p1])
.then((res) => {
console.log("res:", res);
})
.catch((err) => {
console.log(err);
});
/* promiseAll([p3, p2, p4, p1])
.then((res) => {
console.log("res:", res);
})
.catch((err) => {
console.log(err);
}); */
allSettled 方法
allSettled 是为了解决 all 方法的缺陷产生的,主要解决的是不论是否 reject 都把对应的值存起来,因此它没有 catch 方法。
同样的注意事项与 all 相同的不在赘述,只是 allSettled 特别的需要注意没有 reject,因为所有的 promise 值都分为状态保存都 resolve 出去了。还有一点就是Promise.allSettled 不会捕获 throw new Error 的错误
function promiseAllSettled(arr: any[]): Promise<any> {
return new Promise((resolve, reject) => {
// 入参必须保证是数组
if (!Array.isArray(arr)) reject(new Error("传参必须是数组"));
const values: any[] = [];
let counter = 0;
arr.forEach((promise, index) => {
// 传递的参数可以不是Promise 但得经过Promise包装
Promise.resolve(promise)
.then((res) => {
// 数组输出的结果和入参的Promise顺序得一致
values[index] = { status: "fulfilled", value: res };
counter++;
//所有Promise执行完成后 才resolve
if (counter === arr.length) resolve(values);
})
.catch((err) => {
values[index] = { status: "rejected", reason: err };
counter++;
//所有Promise执行完成后 才resolve 没有catch捕获错误
if (counter === arr.length) resolve(values);
});
});
});
}
race 方法
race 是竞速的意思,最快的 promise 解决或者拒绝的时候 Promise.all 的 promise 就解决或者拒绝
function promiseRace(arr: any[]): Promise<any> {
return new Promise((resolve, reject) => {
// 入参必须保证是数组
if (!Array.isArray(arr)) reject(new Error("传参必须是数组"));
arr.forEach((promise) => {
// 传递的参数可以不是Promise 但得经过Promise包装
Promise.resolve(promise)
.then((res) => {
// 数组输出的结果和入参的Promise顺序得一致
//所有Promise执行完成后 才resolve
resolve(res);
})
.catch((err) => {
//所有Promise执行完成后 才resolve 没有catch捕获错误
reject(err);
});
});
});
}
any 方法
any 是为了解决 race 的缺陷,当第一个 promise 解决的时候才 resolve。不论前面是否有 promise 拒绝,只有当所有 promise 都拒绝,才 rejecte
function promiseAny(arr: any[]): Promise<any> {
return new Promise((resolve, reject) => {
// 入参必须保证是数组
if (!Array.isArray(arr)) reject(new Error("传参必须是数组"));
const reasons: any[] = [];
let counter = 0;
arr.forEach((promise, index) => {
// 传递的参数可以不是Promise 但得经过Promise包装
Promise.resolve(promise)
.then((res) => {
//一旦有promise解决就resolve
resolve(res);
})
.catch((err) => {
counter++;
reasons[index] = err;
//所有Promise都reject 才reject
if (counter === arr.length) reject(new AggregateError(reasons));
});
});
});
}