五. 你了解 Promise 吗?平时用的多吗?
5.1 Promise.all 你知道有什么特性吗?
Promise.all 会接受一个 Promise数组,数组里面都是Promise,也可以是其他值,例如常量
-
执行情况:
- Promise数组里的 Promise 全部执行完成之后,才会返回结果;
- 如果其中一个 Promise 执行报错了,整个 Promise.all 会被 catch 住,其他的 Promise 还是会执行的;
- 因为 Promise 是在创建之初就执行了,在实例化的时候就执行了
-
注意:平时看到的 await 或者 .then() 只是为了拿到 Promise 的结果,其实它已经在实例化的时候执行完了
5.2 手写实现 Promise.all
function PromiseAll(promiseArray) {
// 返回的就是一个Promise,因为执行 PromiseAll 后,需要 .then().catch()
return new Promise((resolve,reject) => {
if(!Array.isArray(promiseArray)){
return reject(new Error('传入的必须是数组'));
}
const res = [];
let promiseNums = promiseArray.length;
// 由于考点2,所以需要一个计数器,来记录到底执行完了几个 Promise
const couter = 0;
for (let i = 0; i < promiseNums.length; i++) {
// 考点1:由于传入的 Promise 数组内,可能存在非 Promise 的数据,例如常量等
// !!! 所以需要使用 Promise.resolve(参数a) 方法的特性
// !!! 即 会把包裹的参数a,无论它是什么都会转成一个 Promise
Promise.resolve(promiseArray[i]).then(value => {
// 每一个执行完后就给它 +1
couter++;
// 考点2:结果数组里的数据,应该入传入的 Promise 顺序一一对应
// !!! 不能直接 res.push(value), 可能有执行速度快的 Promise,这样结果数据会乱掉
res[i] = value;
if(counter === promiseNums){
/** 考点3:这里不能直接判断 res.length === promiseNums
* 因为 JS里数组的特性,举例说明
* const array =[];
* array[6] = 'xxx';
* array.length; ===> 7
* */
resolve(res);
}
})
.catch(e => reject(e));
}
})
}
// 测试
const pro1 = new Promise((res,rej) => {
setTimeout(() => {
res('1')
}, 1000);
})
const pro2 = new Promise((res,rej) => {
setTimeout(() => {
res('2')
}, 2000);
})
const pro3 = new Promise((res,rej) => {
setTimeout(() => {
res('3')
}, 3000);
})
const proAll = PromiseAll([pro1,pro2,pro3])
.then(res => {
console.log(res); // 3秒之后打印['1', '2', '3']
})
.catch(e => {
console.log(e);
})
补充
由于Promise是在实例化的时候,就已经执行了。利用这个特性,可以做Promise的缓存。
题:比如一些Promise的结果是固定的,可能你要去调用一个请求,这个请求里请求的是常量。那么你在每一个页面都可能要用到,但是你又没有一个全局的状态管理,导致每一个页面都回去要调一遍接口,会有对于请求的浪费。此时如果不做全局的状态管理,那么可以做一个Promise的缓存,如何做呢?
- 装饰器的含义:
- 对于这个 static async getInfo(){} 方法上,装饰了 enableCache这个装饰器,
- 那么你调用的这个方法就会被存储在 cacheMap 里面,这个 Promise
- 第一次调用还是回去请求,但是之后的调用,不会再去请求服务端,会直接拿到缓存的 Promise
const cacheMap = new Map();
// 装饰器的写法
function enableCache(target, name, descriptor) {
const val = descriptor.value; // 取它最原始的要装饰的函数的值
// 修改原本的函数 ⬇️
descriptor.value = async function(...args){
const cacheKey = name + JSON.stringify(args); // 以请求的名称 name(函数名)以及参数来缓存
if(!cacheMap.get(cacheKey)){ // 如果没有这个 key,则给它set上
// Promise.resolve(val.apply(this,args)) 拿到原来函数要执行的 Promise
const cacheValue = Promise.resolve(val.apply(this,args)).catch(_ => {
cacheMap.set(cacheKey,null);
})
cacheMap.set(cacheKey,cacheValue);
}
return cacheMap.get(cacheKey);
}
return descriptor;
}
class PromiseClass {
@enableCache
static async getInfo(){}
}
PromiseClass.getInfo();
PromiseClass.getInfo();
PromiseClass.getInfo();