假如现在有几十、上百个请求,我们该如何去控制这么高的并发呢?
给你一分钟时间,稍作思考 ~ 🤔
此场景有很多,比如 图片或文件批量下载、RSSHub高速抓取内容。。。
第一想法是不是请求池!!! 没错, 利用Promise
模拟任务队列,从而实现请求池效果。
正文
众所周知,浏览器发起的请求最大并发数量一般都是 6~8
个,这是因为浏览器会限制同一域名下的并发请求数量,以避免对服务器造成过大的压力。
👇 先来模拟一下同时发起大量请求
// 模拟大量请求的函数
async function simulateConcurrentRequests(url, numberOfRequests) {
const requests = Array.from({ length: numberOfRequests }, () => fetch(url));
try {
const responses = await Promise.all(requests);
const results = await Promise.all(responses.map(response => response.json()));
return results;
} catch (error) {
console.error('One or more requests failed:', error);
return [];
}
}
// 使用示例
const url = 'https://jsonplaceholder.typicode.com/posts/1'; // 示例URL
const numberOfRequests = 100; // 请求数量
simulateConcurrentRequests(url, numberOfRequests)
.then(results => {
console.log('All requests completed. Results:', results);
})
.catch(error => {
console.error('Error during requests:', error);
});
👇 效果如下:
一次性并发上百个请求,要是配置低一点,又或者带宽不够的服务器,直接宕机都有可能,所以我们前端这边是需要控制的并发数量去为服务器排忧解难「这样才能突出前端的高级」
首先 什么是队列?
先进先出就是队列,push
一个的同时就会有一个被 shift
。我们看下面的动图可能就会更加的理解:
我们了解了什么是队列,就可以模拟上图的队列行为来实现请求池。
1️⃣ 定义请求池函数,用于控制并发请求数量
// 基于队列控制并发请求数量
async function fetchWithConcurrency(url, totalRequests, maxConcurrency) {
const results = [];
let activeRequests = 0;
let currentIndex = 0;
async function fetchTask() {
if (currentIndex >= totalRequests) return;
const taskIndex = currentIndex++;
activeRequests++;
try {
const response = await fetch(url);
const data = await response.json();
results[taskIndex] = data;
} catch (error) {
console.error('Request failed:', error);
results[taskIndex] = null;
} finally {
activeRequests--;
if (currentIndex < totalRequests) {
await fetchTask();
}
}
}
const initialTasks = Array.from({ length: maxConcurrency }, fetchTask);
await Promise.all(initialTasks);
return results;
}
// 使用示例
const url = 'https://jsonplaceholder.typicode.com/posts/1'; // 示例URL
const totalRequests = 100; // 请求数量
const maxConcurrency = 10; // 并发请求数量限制
fetchWithConcurrency(url, totalRequests, maxConcurrency)
.then(results => {
console.log('All requests completed. Results:', results);
})
.catch(error => {
console.error('Error during requests:', error);
});
代码解释
-
fetchWithConcurrency
函数:用于控制并发请求数量,接收三个参数:url
: 请求的URL。totalRequests
: 总请求数量。maxConcurrency
: 最大并发请求数量。
-
状态变量:
let activeRequests = 0; let currentIndex = 0;
用于跟踪当前活跃的请求数量和当前要处理的请求索引。
-
fetchTask
函数:负责处理单个请求任务。async function fetchTask() { if (currentIndex >= totalRequests) return; const taskIndex = currentIndex++; activeRequests++; try { const response = await fetch(url); const data = await response.json(); results[taskIndex] = data; } catch (error) { console.error('Request failed:', error); results[taskIndex] = null; } finally { activeRequests--; if (currentIndex < totalRequests) { await fetchTask(); } } }
- 如果当前索引超过总请求数,任务结束。
- 增加当前索引并增加活跃请求计数。
- 发送请求并处理响应。
- 无论请求成功与否,最终减少活跃请求计数,并检查是否需要处理下一个请求。
-
启动初始任务:
const initialTasks = Array.from({ length: maxConcurrency }, fetchTask); await Promise.all(initialTasks);
创建一个数组,长度为最大并发数,并启动相应数量的初始任务。使用
Promise.all
等待所有初始任务完成。
2️⃣ 测试
这种方法确保了在任何时间点,最多只有 maxConcurrency
个请求正在进行,从而实现真正的并发控制。
可以看出,请求数确实被控制了,只有有请求响应成功的同时才会有新的请求进来,极大的降低里服务器的压力,后端的同学都只能😲惊呼 6️⃣ 6️⃣ 6️⃣