「面试手写系列」彻底搞懂Promise相关,保熟~

前言

Promise一直是面试中的热点,下面给大家介绍下Promise的相关方法

1. Promise.all

介绍

调用Promise.all时需要传入一个promise数组,我们称它为promiseArr,然后Promise.all会返回一个新的Promise,我们把它称为p

  • 如果promiseArr中有一个Promise失败,则走入pcatch回调中,并拿到对应的错误对象
  • 如果promiseArr里面全成功,则会走入pthen方法中,并能拿到之前传入的promiseArr对应的promise结果

原理

js 复制代码
function promiseAll(promiseArray) {
    return new Promise((resolve, reject) => {
        if(!Array.isArray(promiseArray)) {
            return reject(new Error('传入的参数必须是数组'))
        }
        const promiseNums = promiseArray.length
        const results = new Array(promiseNums)
        let count = 0
        promiseArray.forEach((promise, index) => {
            Promise.resolve(promise).then(res => {
                results[index] = res
                if(++count === promiseNums) {
                    resolve(results)
                }
            })
            .catch(reject)
        })
    })
}

注意点:

  • 需要默认返回一个promise
  • 存结果的results数组需要通过index和传入的promise数组一一对应

应用场景

  • 并行发送多个请求
  • 多个文件的并行读取
  • 并行执行多个任务

2. Promise.race

介绍

race的意思是赛跑,哪个Promise跑的快,也就是哪个Promise最先成功或失败,整个Promise.race也就对应的成功或失败。

原理

js 复制代码
function PromiseRace(promiseArray) {
    return new Promise((resolve, reject) => {
        if(!Array.isArray(promiseArray)) {
            return reject(new Error('传入的参数必须是数组'))
        }
        promiseArray.forEach((promise, index) => {
            Promise.resolve(promise).then(res => {
                resolve(res)
            })
            .catch(reject)
        })
    })
}

应用场景

  • 尽快拿到请求结果 :比如多个接口都可以拿到你想请求的数据,你就可以用Promise.race,如果拿到了一个请求的响应结果,你就可以直接渲染页面了,这样能加快我们的页面响应速度。
  • 多个资源加载 :你可以把手动去加载多个资源,然后用Promise.race来等待最快加载的资源后采取行动,以提高加载性能。
  • 竞态条件处理:同时尝试多个可能的解决方案,并采取第一个可用的解决方案。

3. Promise.prototype.finally

介绍

finallythencatch一样,都是Promise原型上的方法,与thenreject一同,不管Promise成功还是失败,最终都会执行finally方法。

原理

js 复制代码
Promise.prototype.finally = function (cb) {
  return this.then(
    (y) => {
      return Promise.resolve(cb()).then(() => y);
    },
    (r) => {
      return Promise.resolve(cb()).then(() => {
        throw r;
      }); 
    }
  );
};

应用场景

  1. 清理资源或状态 :比如你需要在最Promise执行结束之后释放一些资源,比如打开的文件数据库连接网络连接等,或者释放状态,比如页面的loading
  2. 执行收尾操作 :比如无论Promise成功或者失败,你都要执行一些收尾工作,比如记录日志、发送统计信息或触发一些事件等。
  3. 统一处理 :在thencatch的时候都需要进行的处理,这时候你就不需要写两次重复的代码了,直接放在finally当中。

4. Promise.allSettled

介绍

  1. Promise.allSettled()方法接收一组Promise作为参数,返回一个新的Promise实例
  2. 只有等到所有的这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束
  3. 返回新的Promise实例,一旦结束,状态总是fulfilled,不会变成rejected
  4. 新的promise实例给监听函数传递一个数组results。该数组的每个成员都是一个对象,对应传入Promise.allSettled的Promise实例。每个对象都有status属性,对应着fulfilled和rejected。fulfilled时,对象有value属性,rejected时有reason属性,对应两种状态的返回值。
  5. 当我们不需要关心异步操作的结果,只会关心这些操作有没有结束的时候,这时候Promise.allSettled就派上用场了。

原理

js 复制代码
const formatSettledResult = (isSuccess, value) => {
  return isSuccess ? ({ status: 'fulfilled', value }) : ({ status: 'rejected', reason: value })
}

Promise.all_settled = function (promises) {
  if (!Array.isArray(promises)) {
    throw new Error('传入的参数必须是数组y')
  }
  let num = 0, len = promises.length, results = Array(len)

  return new Promise((resolve, reject) => {
    promises.forEach((promise, index) => {
      Promise.resolve(promise).then((value) => {
        results[index] = formatSettledResult(true, value)
        if (++num === len) {
          resolve(results)
        }
      })
      .catch((reason) => {
        results[index] = formatSettledResult(false, reason)
        if (++num === len) {
          resolve(results)
        }
      })
    })
  })
}

应用场景

  1. 并行请求:当我们需要并行发送多个请求,并在所有请求完成后获得每个请求的结果和状态信息
  2. 批量处理:在等待所有的请求完成后,对各个请求的状态分别进行处理。
  3. 处理多个资源加载:可以将资源的加载封装为promise,并同时加载多个资源,并在加载完后根据promise的状态对每个资源进行对应的处理。

5. Promise.cache

介绍

利用promise实例化立即执行的特性可以做请求的缓存。

原理

js 复制代码
const cacheMap = new Map()
function enableCache(target, name, descriptor) {
    const val = descriptor.value
    descriptor.value = async function (...args) {
        const cacheKey = name + JSON.stringify(args)
        if (!cacheMap.get(cacheKey)) {
            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()

这里我先定义一个Map作为缓存对象,然后用方法名,也就是上面的getInfo + 参数序列化后的值作为缓存key值,然后就可以实现请求的缓存了,当然我这里缓存key设置的比较简单,实际业务场景肯定会更为严谨一些,然后业务中如果用了缓存的话,需要考虑缓存失效的问题,过快失效和过久不失效都可能会让程序出现bug,需要注意一下。

6. Promise.limit

介绍

Promise.limit可以通过promise实现并发控制

原理

js 复制代码
function limitLoad(promiseArray, limit) {
    const results = [];
    const promises = promiseArray.slice(0, limit).map((promise, index) => {
        return promise.then((value) => {
            results[index] =  value;
            return index;
        })
    })
    let p = Promise.race(promises)
    for(let i = limit; i < promiseArray.length; i++) {
        p = p.then((index) => {
            promises[index] = promiseArray[i].then((value) => {
                results[i] = value;
                return value
            })
            return Promise.race(promises)
        })
    }
    return p.then(() => results);
}

首先,声明一个results数组存储promise结果,然后先取出limit个数的promise,通过Promise.race可以拿到最快执行完的那一个,我们前面会把每个promise对应在promises数组的位置index往下传递,然后通过循环串成一个promise链,在Promise.race的then方法中,通过前面的index找到那个最快执行完的promise所在的位置,将其替换,最终promise链执行完将存储promise结果results数组返回就行了。

应用场景

  1. 并发请求控制 :有时候为避免服务器性能问题,可以使用Promise.limit进行并发控制,以提高请求的响应速度
  2. 批量处理限制 :当对大量数据进行处理时,有时候一次性处理会导致服务器内存溢出,这时可以采用Promise.limit控制同时处理的数据数量,以提高资源的处理效率。
  3. 队列调度 :在任务调度和队列管理中,可以使用Promise.limit将所有任务放在固定大小的任务池中,并限制同时执行的任务数量,这样可以确保任务按照限制的并发度进行顺序执行,避免资源竞争和过度负载

小结

以上给大家介绍了6个Promise相关方法以及应用场景,马上快金九银十了,希望大家都能熟练掌握好promise相关,出去面试offer拿到手软

如果还有兴趣想掌握手写Promise"吊打面试官",可以移步我之前写的文章,手写Promise

相关推荐
恋猫de小郭19 分钟前
AI 可以让 WIFI 实现监控室内人体位置和姿态,无需摄像头?
前端·人工智能·ai编程
哀木24 分钟前
给自己整一个 claude code,解锁编程新姿势
前端
程序员鱼皮29 分钟前
GitHub 关注突破 2w,我总结了 10 个涨星涨粉技巧!
前端·后端·github
UrbanJazzerati31 分钟前
Vue3 父子组件通信完全指南
前端·面试
是一碗螺丝粉41 分钟前
5分钟上手LangChain.js:用DeepSeek给你的App加上AI能力
前端·人工智能·langchain
wuhen_n42 分钟前
双端 Diff 算法详解
前端·javascript·vue.js
UrbanJazzerati42 分钟前
Vue 3 纯小白快速入门指南
前端·面试
雮尘43 分钟前
手把手带你玩转Android gRPC:一篇搞定原理、配置与客户端开发
android·前端·grpc
光影少年43 分钟前
说说闭包的理解和应用场景?
前端·javascript·掘金·金石计划
是一碗螺丝粉44 分钟前
LangChain 核心组件深度解析:模型与提示词模板
前端·langchain·aigc