最近师兄面试时遇到了一道手撕题:有一url数组,其中有若干url请求,有一变量
maxNum,表示最大并发的url请求数量,实现一个方法,完成对url的并发控制,将每个url的请求结果合并到一个数组中,并使其在数组中的位置与url在请求数组中的位置对应
大致思路是按照要求开始时从url数组中拿出 maxNum 个请求并发送,当其中有请求完成时,再从url数组中拿出新的请求填补,并将已完成请求的结果写入结果数组对应的位置,这样依次类推,直到urls里面的所有请求都执行完才终止
其实重点就两个:
一是保证请求的并发数量,当有请求完成时,开启新的请求进行补位
二是确保在请求结果异步返回的情况下,保证存储结果的顺序正确
直接上代码:
function concurRequests(urls,maxNum) {
return new Promise(resolve=>{
if(urls.length ===0){//如果urls为空,直接返回空数组
resolve([])
return
}
const results = []
let index = 0//下一个待发送请求的下标
let count = 0//已完成请求的数量
async function sendRequest() {
if(index === urls.length) return //当所有请求已发送时,不再发送请求
const i = index //保存发送请求的下标,在接收结果时保证其存储位置与在urls数组中的位置对应
const url = urls[index]
index++
try{
const response = await fetch(url)
results[i] = response
}catch(err){
results[i] = err
}finally{
count++//每完成一次请求让count++
if(count === urls.length) {//所有请求完成时,返回results
console.log('所有请求已完成')
resolve(results)
}
sendRequest()//否则继续发送下一个请求
}
}
const times = maxNum < urls.length ? maxNum : urls.length//考虑特殊情况,当最大并发数大于url数组总长度
for (let i = 0; i < times; i++) {
sendRequest()
}
})
}
还需要注意的是对几种特殊情况的处理:
1.urls的长度为0时,results就没有值,此时应该返回空数组
2.maxNum大于urls的长度时,应该取的是urls的长度,否则则是取maxNum
3.对于请求结果错误的情况,也应该保存其错误结果
简单的测试一下:
const urls = [];
for (let i = 1; i <= 50; i++) {//总共50个请求
urls.push(`https://jsonplaceholder.typicode.com/todos/${i}`);
}
concurRequests(urls, 5).then(res => {//每次并发5个请求
console.log(res);
})
可以看到每次并行发送5个请求
结果保存也没有问题