js并发请求示例demo
简单版本(不考虑请求失败后的重试)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
/**
* 并发请求函数
* @param {Array} urls - 要请求的url数组
* @returns {Number} maxNum - 最大并发数
*/
const concurRequest = (urls, maxNum) => {
if(urls.length === 0){
return Promise.resolve([])
}
return new Promise((resolve) => {
const results = [] // 存储所有请求的结果
let nextIndex = 0 // 下一个要请求的url索引
// 发送下一个请求,并将请求结果存储在result数组中
const _request = async () => {
if(nextIndex >= urls.length) return;
const i = nextIndex;
const url = urls[nextIndex++]
const resp = await fetch(url);
results[i] = resp;
// 过滤无效数据,过滤null、undefined、空字符串、空数组、空对象
const cleanResults = arr.filter(item => {
if (!item) return false; // 过滤 null、undefined、空字符串
if (Array.isArray(item) && item.length === 0) return false; // 过滤空数组
if (typeof item === 'object') {
return Object.keys(item).length > 0; // 过滤空对象
}
return true; // 其他认为是有效数据
});
if(cleanResults.length === urls.length){
resolve(results);
return;
}
_request() // 递归调用_request函数,发送下一个请求
}
// 启动并发请求
for(let i = 0; i < Math.min(maxNum, urls.length); i++){
_request();
}
})
}
// 测试
const urls = [
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php'
]
// 测试没问题,并发请求是5个
concurRequest(urls, 5).then(res => {
console.log(res)
})
</script>
</body>
</html>考虑请求失败后的重试版本和重试间隔
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
/**
* 并发请求函数(支持失败重试)
* @param {Array} urls - 要请求的url数组
* @param {Number} maxNum - 最大并发数
* @param {Number} retryTimes - 最大重试次数(默认0)
* @param {Number} retryDelay - 重试间隔(毫秒,默认1000)
* @returns {Promise} - 返回所有请求的结果数组
*/
const concurRequest = (urls, maxNum, retryTimes = 0, retryDelay = 1000) => {
if(urls.length === 0){
return Promise.resolve([])
}
return new Promise((resolve) => {
const results = [] // 存储所有请求的结果
let nextIndex = 0 // 下一个要请求的url索引
let completedCount = 0 // 已完成的请求数量
// 延迟函数
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// 带重试机制的请求函数
const fetchWithRetry = async (url, currentRetry = 0) => {
try {
const resp = await fetch(url);
// 检查响应是否成功(200-299状态码)
if (!resp.ok) {
throw new Error(`HTTP error! status: ${resp.status}`);
}
return resp;
} catch (error) {
// 如果还有重试次数,就延迟后重试
if (currentRetry < retryTimes) {
await delay(retryDelay);
return fetchWithRetry(url, currentRetry + 1);
}
// 重试次数用尽,返回错误
return { error: error.message, url };
}
};
// 发送下一个请求
const _request = async () => {
if(nextIndex >= urls.length) return;
const i = nextIndex;
const url = urls[nextIndex++]
try {
const resp = await fetchWithRetry(url);
results[i] = resp;
} catch (error) {
results[i] = { error: error.message, url };
} finally {
completedCount++;
if(completedCount === urls.length){
resolve(results);
return;
}
_request() // 递归调用,继续处理下一个请求
}
}
// 启动并发请求
for(let i = 0; i < Math.min(maxNum, urls.length); i++){
_request();
}
})
}
// 测试
const urls = [
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php',
'https://v.api.aa1.cn/api/yiyan/index.php',
'https://v.api.aa1.cn/api/chinaip/',
'https://v.api.aa1.cn/api/api-wenan-qg/index.php?aa1=json',
'https://v.api.aa1.cn/api/api-tx/index.php?wpon=json',
'https://v.api.aa1.cn/api/tiangou/index.php'
]
// 测试没问题,请求结果500的重试了两次,加上首次总共3次,并发请求是5个,并且重试的间隔是2秒
concurRequest(urls, 5, 2, 2000).then(res => {
console.log(res)
})
</script>
</body>
</html>