前端高频面试题之Promise相关方法
2026/3/17 5:44:55 网站建设 项目流程

前言

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。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询