2021大厂前端面试核心面试题详解一 (3)

五. 你了解 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();
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值