如何实现并发请求数量控制?

前言

本文为转载文章,原文地址:关于前端:如何实现并发请求数量控制?

对文章作者表示感谢。

文章代码实现部分根据理解做了部分修改,整体效果无差异。

场景

假设有这么一个场景:现在有20个异步请求需要发送,但是由于某些原因,要求我们必须将同一时刻的并发请求数量控制在3个以内,并且还要尽可能快速的拿到响应结果。其实这个场景在一些大厂的面试题中也有过提及,如下:

1
2
3
4
实现一个并发请求函数 concurrencyRequest(urls, maxNum),要求如下:
• 要求最大并发数 maxNum
• 每当有一个请求返回,就留下一个空位,可以增加新的请求
• 所有请求完成后,结果按照 urls 里面的顺序依次打出(发送请求的函数可以直接使用fetch即可)

设计思路

首先来看将上面的文字转化为图之后的效果:

这样就直观的看到,有一个最大并发数maxNum20个异步请求的urls集合和并发返回之后的results集合。

下面就开始演示这个思路是如何开始的,如下:

首先按照每次只能并发3个请求的要求,这里就对应A、B、C,当其中有一个请求完之后就会再从urls里面再取出一个进行请求,这样依次类推,直到urls里面的20个请求都执行完才终止请求。

主要思路就是上面所述,但是在开发时我们要考虑一些特殊情况,如下:

  • urls的长度为0时,results就没有值,此时应该返回空数组
  • maxNum大于urls的长度时,应该取的是urls的长度,否则则是取maxNum
  • 需要定义一个count计数器来判断是否已全部请求完成
  • 因为没有考虑请求是否请求成功,所以请求成功或报错都应把结果保存在results集合中
  • results中的顺序需和urls中的保持一致

开发

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const concurrencyRequest = (urls, maxNum) => {
return new Promise((resolve) => {
if (urls.length === 0) {
resolve([]);
return;
}
const results = [];
let index = 0;
let count = 0; // 表示已完成(成功 + 失败) 请求的数目

async function request () {
if(index === urls.length) return;
const i = index;
const url = urls[index];
index++;
try {
const res = await fetch(url);
const json = await res.json();
results[i] = json;
} catch (e) {
results[i] = e;
} finally {
count++;
if(count === urls.length) {
console.log('Done');
resolve(results);
}
request();
}
}

const times = Math.min(maxNum, urls.length);
for(let i = 0; i < times; i++) {
request();
}
})
}

测试

在上面代码下添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
(async () => {
const urls = [];
const maxNum = 3;
for (let i = 1; i <= 20; i++) {
urls.push(`https://jsonplaceholder.typicode.com/todos/${i}`);
}
console.time('request');
const res = await concurrencyRequest(urls, maxNum);
console.timeEnd('request');
console.log(res);
})()

结果