<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>并发请求控制示例</title>
<!-- 注意:实际项目中index.js应该包含concurRquest函数的实现 -->
<script src="./index.js"></script>
</head>
<body>
<script>
// 生成测试URL数组
const urls = Array.from({length: 100}, (_, i) => `http://localhost:3000/api?id=${i + 1}`);
// 调用并发请求函数,最大并发数为12
concurRquest(urls, 12)
.then(res => {
console.log('所有请求完成:', res);
})
.catch(error => {
console.error('请求过程中出错:', error);
});
</script>
</body>
</html>
/**
* 并发请求控制函数
* @param {string[]} urls 请求的URL数组
* @param {number} maxNum 最大并发数
* @returns {Promise<any[]>} 返回一个Promise,resolve时返回所有请求结果的数组
*/
function concurRquest(urls, maxNum) {
return new Promise((resolve, reject) => {
// 参数校验
if (!Array.isArray(urls) || urls.length === 0) {
resolve([]);
return;
}
if (typeof maxNum !== 'number' || maxNum <= 0) {
reject(new Error('maxNum必须为正数'));
return;
}
const results = []; // 存储所有请求结果
let count = 0; // 已完成的请求数量
let index = 0; // 当前要处理的URL索引
let hasError = false; // 标记是否发生错误
// 执行单个请求的辅助函数
async function run() {
if (hasError) return; // 如果已有错误发生,不再继续新请求
const currentIndex = index++; // 获取当前请求的索引
// 所有URL已处理完毕
if (currentIndex >= urls.length) {
return;
}
const url = urls[currentIndex];
try {
console.log(`开始请求: ${url}`);
const response = await fetch(url);
// 检查响应状态
if (!response.ok) {
throw new Error(`请求失败,状态码: ${response.status}`);
}
// 假设我们想要JSON格式的响应数据
const data = await response.json();
results[currentIndex] = data;
} catch (error) {
hasError = true; // 标记错误状态
reject(error); // 立即拒绝整个Promise
return;
} finally {
// 无论成功失败,都增加完成计数
if (!hasError) {
count++;
// 所有请求完成
if (count === urls.length) {
resolve(results);
}
// 启动下一个请求
run();
}
}
}
// 启动初始批次请求
const initialConcurrency = Math.min(maxNum, urls.length);
for (let i = 0; i < initialConcurrency; i++) {
run();
}
});
}
核心原理
-
维护一个"请求池",初始时启动最大并发数允许的请求
-
每个请求完成后,无论成功失败,都从剩余的URL中取出下一个进行请求
-
使用计数器跟踪已完成的请求数量,当所有请求完成时resolve最终的Promise
-
使用索引确保返回结果的顺序与输入URL顺序一致
使用场景
-
需要批量获取数据的场景
-
需要限制同时发起的请求数量以避免浏览器限制
-
需要对大量请求结果进行有序处理的场景
注意事项
-
根据API特性可能需要调整错误处理逻辑
-
对于特别大量的请求,可以考虑分批次处理以避免内存问题