在掘金潜水🤿一年多了,昨晚突发奇想,大概实现了控制并发
常见的场景题之一。 对于这个场景应该如何去设置?
我的想法是这样子。 首先取出控制数量个数的请求,然后并发的开启任务,等待任务完成之后(无论成功还是失败,然后添加下一个任务) 同时我们需要保证发起请求的顺序与得到响应的顺序是一致的。
需要拿到下标这样子,我们通过 res来存储promise,以便查看得到的各个请求的下标是否对应之前的promise
为什么这样子,因为在实际中可能出现很多请求,比如大文件分片上传,就需要确认哪一个分片对应哪一个索引,否则等上传成功之后就无法成功拼接出正确的文件
如何实现完成一个就取出来呢,这里可以使用map存储promise和其下标的关系
let pool = new Map()
且因为我们完成之后就要取出来,因此就可以删除掉promise索引
方向明确现在开搞
async function async_pool_Promise(PromiseList,limit){
const results = []//存放结果的
const pool = new Map()//存放promise与其索引关系的。
}
这里我们采用什么来拿到各个promise。 递归?递归不可中断,且不太可控。且多次递归会使得调用栈极其臃肿,因此尽可能避免 因此我们采用循环的方式来控制 如何一个数组确定最快完成的,无论成功或者失败——Promise.race()
PS:这里可能有小伙伴不知道Promise.race(),这个方法是传入一个promise数组,返回最快完成的那一个,无论成功还是失败
for(let i = 0; i <= PromiseList; i++){
if(pool.size > limit){
await Promise.race(pool.keys())
}
}
为什么这里需要这样 我们可以这样理解
这时候就需要最快那一个出现啦
这样就完成了一次更替
当后者进入pool时,又满足if条件,又进入等待
那么对于一个promise完成之后要干嘛? 是不是要将结果添加到results中,然后将自己在pool中的痕迹清空。
如何实现自动? promise有then方法 通过promise的then方法,注册事件实现完成后自动赋值,以便确认哪个promise对应哪一个序号
const cb = (value,reason)=>{
if (reason) {
res[pool.get(promise)] = { status: 'rejected', reason };
}else{
res[pool.get(promise)] = { status: 'fulfilled', value };
}
pool.delete(promise)
}
promise.then(value=>cb(value),reason=>cb(null,reason))
这样当失败时添加一个对象,成功时添加一个对象
那么大概代码就成功了
async function async_pool_Promise2(requestList, limits) {
const res = []
const promises = []
const pool = new Map()//为了存储索引
for (let i = 0; i < requestList.length; i++) {
if (pool.size >= limits) {
await Promise.race(pool.keys()).catch(err => err)
}
const promise = requestList[i]
const cb = (value,reason) => {
if (reason) {
res[pool.get(promise)] = { status: 'rejected', reason };
}else{
res[pool.get(promise)] = { status: 'fulfilled', value };
}
pool.delete(promise)
}
promise.then(value=>cb(value), reason=>cb(null,reason))
pool.set(promise, i)
promises.push(promise)
}
return res;
}
这样子对吗? 其实还有些欠缺
比如最后pool池子的还没结束诶
那么等待池子中的所有promise完成可以使用 Promise.allSettled(pool)
PS:Promise.allSettled(),是传入一个promise数组,然后当这个promise数组中的所有promise的状态都改变
才结束。为什么这里不使用Promise.all()? 因为Promise.all()是如果有一个失败就会结束,那么其他的promise状态就可能未知
那么完整代码如下
async function async_pool_Promise2(requestList, limits) {
const res = []
const pool = new Map()//为了存储索引
for (let i = 0; i < requestList.length; i++) {
if (pool.size >= limits) {
await Promise.race(pool.keys()).catch(err => err)
}
const promise = requestList[i]
const cb = (value,reason) => {
if (reason) {
res[pool.get(promise)] = { status: 'rejected', reason };
}else{
res[pool.get(promise)] = { status: 'fulfilled', value };
}
pool.delete(promise)
}
promise.then(value=>cb(value), reason=>cb(null,reason))
pool.set(promise, i)
}
await Promise.allSettled(pool.keys())//剩下的交给allSettled处理
return res;
}
async_pool_Promise2(promises, 5).then(results => {
console.log(results);
})
欢迎👏各位大佬斧正🙏