前言
Promise
一直是面试中的热点,下面给大家介绍下Promise的相关方法
。
1. Promise.all
介绍
调用Promise.all
时需要传入一个promise数组
,我们称它为promiseArr
,然后Promise.all
会返回一个新的Promise
,我们把它称为p
:
- 如果
promiseArr
中有一个Promise
失败,则走入p
的catch
回调中,并拿到对应的错误对象
。 - 如果
promiseArr
里面全成功,则会走入p
的then
方法中,并能拿到之前传入的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
介绍
finally
和then
、catch
一样,都是Promise原型
上的方法,与then
、reject
一同,不管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;
});
}
);
};
应用场景
- 清理资源或状态 :比如你需要在最Promise执行结束之后释放一些资源,比如
打开的文件
、数据库连接
或网络连接
等,或者释放状态,比如页面的loading
等 - 执行收尾操作 :比如无论Promise成功或者失败,你都要执行一些收尾工作,比如
记录日志、发送统计信息或触发一些事件
等。 - 统一处理 :在
then
和catch
的时候都需要进行的处理,这时候你就不需要写两次重复的代码了,直接放在finally当中。
4. Promise.allSettled
介绍
- Promise.allSettled()方法接收一组Promise作为参数,
返回一个新的Promise实例
- 只有等到所有的这些参数实例都返回结果,
不管是fulfilled还是rejected,包装实例才会结束
- 返回新的Promise实例,一旦结束,状态
总是fulfilled,不会变成rejected
- 新的promise实例给监听函数传递一个数组results。该数组的每个成员都是一个对象,对应传入Promise.allSettled的Promise实例。每个对象都有status属性,对应着fulfilled和rejected。fulfilled时,对象有value属性,rejected时有reason属性,对应两种状态的返回值。
- 当我们不需要关心异步操作的结果,只会关心这些操作有没有结束的时候,这时候
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)
}
})
})
})
}
应用场景
- 并行请求:当我们需要并行发送多个请求,并在所有请求完成后获得每个请求的结果和状态信息
- 批量处理:在等待所有的请求完成后,对各个请求的状态分别进行处理。
- 处理多个资源加载:可以将资源的加载封装为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
数组返回就行了。
应用场景
- 并发请求控制 :有时候为避免
服务器性能问题
,可以使用Promise.limit
进行并发控制
,以提高请求的响应速度 - 批量处理限制 :当对
大量数据
进行处理时,有时候一次性处理会导致服务器内存溢出,这时可以采用Promise.limit
控制同时处理的数据数量
,以提高资源的处理效率。 - 队列调度 :在任务调度和队列管理中,可以使用
Promise.limit
将所有任务放在固定大小的任务池
中,并限制同时执行的任务数量,这样可以确保任务按照限制的并发度进行顺序执行,避免资源竞争和过度负载
。
小结
以上给大家介绍了6个Promise相关方法以及应用场景,马上快金九银十
了,希望大家都能熟练掌握好promise相关,出去面试offer拿到手软
!
如果还有兴趣想掌握手写Promise
,"吊打面试官"
,可以移步我之前写的文章,手写Promise。