news 2026/6/13 13:59:55

前端高频面试题之Promise相关方法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
前端高频面试题之Promise相关方法

前言

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

1. Promise.all

1.1 介绍

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

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

1.2 原理

functionpromiseAll(promiseArray){returnnewPromise((resolve,reject)=>{if(!Array.isArray(promiseArray)){returnreject(newError('传入的参数必须是数组'))}constpromiseNum=promiseArray.lengthconstresults=newArray(promiseNum)letcount=0promiseArray.forEach((promise,index)=>{Promise.resolve(promise).then(res=>{results[index]=resif(++count===promiseNum){resolve(results)}}).catch(reject)})})}

注意点:

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

1.3 应用场景

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

2. Promise.race

2.1 介绍

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

2.2 原理

functionPromiseRace(promiseArray){returnnewPromise((resolve,reject)=>{if(!Array.isArray(promiseArray)){returnreject(newError('传入的参数必须是数组'))}promiseArray.forEach((promise,index)=>{Promise.resolve(promise).then(res=>{resolve(res)}).catch(reject)})})}

2.3 应用场景

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

3. Promise.prototype.finally

3.1 介绍

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

3.2 原理

Promise.prototype.finally=function(cb){returnthis.then((y)=>{returnPromise.resolve(cb()).then(()=>y);},(r)=>{returnPromise.resolve(cb()).then(()=>{throwr;});});};

3.3 应用场景

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

4. Promise.allSettled

4.1 介绍

  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就派上用场了。

4.2 原理

constformatSettledResult=(isSuccess,value)=>{returnisSuccess?({status:'fulfilled',value}):({status:'rejected',reason:value})}Promise.all_settled=function(promises){if(!Array.isArray(promises)){thrownewError('传入的参数必须是数组y')}letnum=0,len=promises.length,results=Array(len)returnnewPromise((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)}})})})}

4.3 应用场景

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

5. Promise.cache

5.1 介绍

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

5.2 原理

constcacheMap=newMap()functionenableCache(target,name,descriptor){constval=descriptor.value descriptor.value=asyncfunction(...args){constcacheKey=name+JSON.stringify(args)if(!cacheMap.get(cacheKey)){constcacheValue=Promise.resolve(val.apply(this,args)).catch(()=>{cacheMap.set(cacheKey,null)})cacheMap.set(cacheKey,cacheValue)}returncacheMap.get(cacheKey)}returndescriptor}classPromiseClass{// 装饰器// @enableCachestaticasyncgetInfo(){}}PromiseClass.getInfo()PromiseClass.getInfo()PromiseClass.getInfo()

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

5.3 应用场景

  • 缓存接口请求结果,避免重复请求。
  • 也可以用来缓存复杂函数计算结果,提高性能。

6. Promise.limit

6.1 介绍

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

6.2 原理

functionlimitLoad(promiseArray,limit){constresults=[];constpromises=promiseArray.slice(0,limit).map((promise,index)=>{returnpromise.then((value)=>{results[index]=value;returnindex;})})letp=Promise.race(promises)for(leti=limit;i<promiseArray.length;i++){p=p.then((index)=>{promises[index]=promiseArray[i].then((value)=>{results[i]=value;returnvalue})returnPromise.race(promises)})}returnp.then(()=>results);}

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

6.3 应用场景

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

7. Promise.try

7.1 介绍

Promise.try可以把一个函数包装为一个 Promise。

functiontest(a,b){console.log('调用test',a,b)return'test res';}Promise.try(test,'a','b').then((res)=>{console.log('then',res)}).catch(()=>{console.log('catch');})/** * 打印结果: * 调用test a b * then test res */

它也支持传入异步函数,比如async函数。

constp=newPromise((resolve,reject)=>{setTimeout(()=>{resolve(111);},3000)})asyncfunctiontest(a,b){awaitp;console.log('test',a,b)return'test res';}Promise.try(test,'a','b').then((res)=>{console.log('then',res)}).catch(()=>{console.log('catch');})/** * 先等待3s,然后打印结果如下: * 调用test a b * then test res */

7.2 原理

Promise.try=function(fn,...args){returnnewPromise((resolve,reject)=>{try{resolve(fn(...args))}catch(e){reject(e)}})}

Promise.try的函数签名为Promise.try(func, arg1, arg2, ...argN),它会把arg1, arg2, ...argN作为参数传递给func, 并以同步的方式立即执行func函数,最后将其结果包装为一个Promise对象。

7.3 应用场景

  • 它可以把一个函数包装为一个Promise对象,比new Promise((resolve) => resolve(func()))更加简洁。

8. Promise.withResolvers

8.1 介绍

Promise.withResolvers函数经调用会返回一个新的Promise,以及resolvereject方法。

const{promise,resolve,reject}=Promise.withResolvers()

8.2 原理

Promise.withResolvers=function(){letresolve,reject;constpromise=newPromise((res,rej)=>{resolve=res;reject=rej;});return{promise,resolve,reject};}

8.3 应用场景

  • 可以替代new Promise创建Promise对象,使得promiseresolvereject三个变量在同一个作用域中,方便管理。

小结

以上给大家介绍了8个 Promise 相关方法以及各自应用场景,其中需要注意Promise.tryPromise.withResolvers属于比较新的API,使用时需注意其兼容性。

如果还有兴趣想掌握手写Promise的话,可以移步我之前写的文章,前端高频面试题之手写Promise。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/12 16:16:19

EventBus源码主要流程解析

首先从最基本的EventBus类的register()看实现逻辑&#xff1a;1. 订阅事件通过一个SubscriberMethodFinder类查找对应订阅的方法&#xff0c;然后进行订阅。public void register(Object subscriber) {if (AndroidDependenciesDetector.isAndroidSDKAvailable() && !An…

作者头像 李华
网站建设 2026/6/11 22:34:13

游戏帧率优化大师指南:从基础配置到专业调优的完整路径

游戏帧率优化大师指南&#xff1a;从基础配置到专业调优的完整路径 【免费下载链接】genshin-fps-unlock unlocks the 60 fps cap 项目地址: https://gitcode.com/gh_mirrors/ge/genshin-fps-unlock 您是否在热门游戏中遭遇画面卡顿、操作延迟的困扰&#xff1f;当高性能…

作者头像 李华
网站建设 2026/6/9 22:35:27

36、探索CDF:网络频道订阅与管理全攻略

探索CDF:网络频道订阅与管理全攻略 1. 网络订阅的优势与工具对比 在网络浏览中,用户希望具备以下能力: - 更好地跟踪所订阅的网站。 - 当频道或收藏夹更新时接收通知。 - 在线或离线查看系统上的内容。 曾经,Netscape仅提供原始的手动网站/书签检查工具。虽然它最近开…

作者头像 李华
网站建设 2026/6/12 10:59:44

39、深入解析SOAP、UDDI与WSDL:构建Web服务通信基石

深入解析SOAP、UDDI与WSDL:构建Web服务通信基石 1. 引言 在当今的互联网世界中,Web服务的重要性日益凸显。而Simple Object Access Protocol(SOAP)作为一种用于在Web服务之间传递XML消息的流行协议,备受关注。但SOAP并非孤立存在,它是由Web Services Description Langu…

作者头像 李华
网站建设 2026/6/12 21:06:05

42、数学标记语言 MathML 全解析

数学标记语言 MathML 全解析 1. 数学表达式标记的发展与问题 从 20 世纪 80 年代至今,不少文字处理和图形应用程序具备了创建数学和科学表达式的能力,这些表达式通常会被转换为专有格式或图形格式(如 JPEG、GIF 或 TIFF)。然而,这些发展仅能实现视觉呈现,无法传达数学和…

作者头像 李华
网站建设 2026/6/13 0:50:23

5分钟学会:飞书文档批量导出完整指南

5分钟学会&#xff1a;飞书文档批量导出完整指南 【免费下载链接】feishu-doc-export 项目地址: https://gitcode.com/gh_mirrors/fe/feishu-doc-export 还在为飞书文档迁移而烦恼吗&#xff1f;feishu-doc-export工具帮你解决所有痛点&#xff0c;只需简单配置就能将飞…

作者头像 李华