前言
此题为如何处理并发请求,意思就是让你完成一个函数,传入一个url地址的数组,再传入一个最大的并发数,去完成请求。
图解
一个url数组,里面的每一项都是一个url地址,然后给一个最大的并发数maxNum,例如给我最大的并发数是3(maxNum=3),在发送请求的时候,可以同时最多发送3个请求。当请求C完成/结束了,现在的并发数只剩下AB了,然后用后面的D来补位,直到把所有的请求都发出,最后将每一个请求的结果都放到一个数组里面去(无论成功还是失败,失败将错误信息存入数组),而数组的顺序要与url地址的顺序保持一致。最后将这个数组返回。
需求分析
异步请求
函数肯定是返回一个promise,而且这个promise它的状态肯定是完成的。不可能是拒绝状态
function concurRequest(urls,maxNum){
return new Promise(resolve=>{
})
}
特殊情况
上面图可以看出,要将每个请求的结果都存入数组中。先排除特殊情况,当传入的数组为空,此时就不用请求
function concurRequest(urls,maxNum){
return new Promise(resolve=>{
// 特殊情况:当url数组为空,不发请求
if (urls.length===0) {
resolve([])
return;
}
const results =[];
})
}
正常情况
题目的意思,无非就是从url数组中依次取出元素来进行发送请求,当某个请求完成了再取出来发送一个请求,以此类推。在这一循环中有一个重复的动作:取出url数组的某一项,然后发送请求。那此时我们可以定义一个变量index,就是这个变量它指着哪一个下标,我就取哪一项,当取完这一项后,变量index加1,继续往后取
function concurRequest(urls, maxNum) {
return new Promise(resolve => {
// 特殊情况:当url数组为空,不发请求
if (urls.length === 0) {
resolve([])
return;
}
const results = [];
let index = 0;//下一次请求的下标
// 发送请求函数
function request() {
const url = urls[index]; //发送请求地址
index++;
console.log(url);
fetch(url)
}
// 模仿五次请求
request();
request();
request();
request();
request();
})
}
准备数据
<script>
// 模拟 将urls存放100个url地址,最高并发数5个
const urls=[];
for(let i =0;i<=100;++i){
urls.push(`https://jsonplaceholder.typicode.com/todos/${i}`)
}
concurRequest(urls,5).then((res)=>{
console.log(res);
})
</script>
输出结果
由结果可以看出,已经发送了5次请求,就是把url数组里边前五个都拿出来了,发送出去了,那接下来要做的就是将这个请求的结果加入到结果集里面,而且无论成功还是错误都要加入这个结果集里面,那么怎么加入呢?push行吗?答案是不行,因为push有可能会改变它的顺序(结果集),为什么?因为我们不知道哪个请求会先完成。那怎么实行呢? 我们可以通过发送到哪个请求的时候,它所在的下标,然后我用对应的下标将其放到该下标位置,那下标是谁呢?变量index? 很显然不是,因为index在不断的往后变动,例如,当C完成的时候,index早就不在这个位置了,那怎么办呢?最简单的就是将当前的index保存起来
//request函数修改
async function request() {
const i = index; //存当前index
const url = urls[index]; //发送请求地址
index++;
try {
const resp = await fetch(url);
results[i] = resp
} catch (err) {
results[i] = err
}
console.log(results);
}
输出结果
可以看出,A先完成,其他是空的,然后又有两个完成,中间两个是空的,
而且可以看出,结果集的顺序跟请求顺序是一样的,接下来要做的呢,就是当这个请求完成了,要继续发送下一个请求了。
async function request() {
const i = index; //存当前index
const url = urls[index]; //发送请求地址
index++;
console.log(url);
try {
const resp = await fetch(url);
results[i] = resp
} catch (err) {
results[i] = err
} finally {
// 完成之后发送下一个请求
request()
}
}
由输出结果看出,这个并发会一直执行下去,但是此时超过了url地址,它还是会继续发送请求,这时我们就要做出限制了
做限制之后,我们要考虑一下它怎么调用呢,调用时还要考虑一个问题,如果传入的url数组长度比最大并发数小呢?那也要做一层限制,最终结果如下
function concurRequest(urls, maxNum) {
return new Promise(resolve => {
// 特殊情况:当url数组为空,不发请求
if (urls.length === 0) {
resolve([])
return;
}
const results = [];
let index = 0;//下一次请求的下标
let count = 0;//请求完成的数量
// 发送请求函数
async function request() {
// 当index等于数组长度时结束
if (index === urls.length) {
return
}
const i = index; //存当前index
const url = urls[index]; //发送请求地址
index++;
try {
const resp = await fetch(url);
results[i] = resp
} catch (err) {
results[i] = err
} finally {
// 判断是否所有请求都完成了
count++;
if (count === urls.length) {
console.log("请求完了");
resolve(results)
}
// 完成之后发送下一个请求
request()
}
}
// 防止最大并发数小于url长度的错误
const timers = Math.min(maxNum, urls.length)
for (let i = 0; i < timers; i++) {
request();
}
})
}
输出结果
结尾
本题到这就结束了,上面有什么不足之处,希望各位前端大佬指出