前端并发通常涉及到同时发起多个网络请求、处理多个异步操作或同时执行多个JavaScript任务。今天和大家分享一下,如何设计一个可以控制并发数量的函数。
为了合理利用浏览器的请求并发数,我们一般会根据具体场景使用不同策略的并发请求:
- 自定义并发数量,单次请求成功之后,将后续请求依次放入请求队列里,保持固定的请求数量。如大文件切片上传,保证请求数量的同时,节省后续请求的时间。
- 限制并发数量,将请求分割成多个数组,并发数组内请求成功之后,在推入下一个请求数组,保证每次固定的请求数量。如模块内同时需要发送多个请求时导致的请求拥塞。
自定义高并发请求
示例图
并发数量5个
请求成功之后在推入后续的请求,始终保持5个并发数
代码如下
/**
* 并发请求函数
* @param urls - 请求的URL数组
* @param count - 并发请求的数量
* @returns 一个Promise,当所有请求完成时解析为结果数组
*/
export function concurrencyRequests(urls: string[], count: number) {
// 如果URL数组为空,直接返回空数组Promise
if(urls.length === 0) return Promise.resolve([]);
// 返回一个新的Promise,用于处理并发请求
return new Promise(async (resolve, reject) => {
const results: any[] = []; // 存储请求结果的数组
let currentCount = 0; // 当前并发请求数
/**
* 递归函数,用于处理下一个请求
*/
const next = async() => {
// 如果当前并发请求数大于等于URL数组长度,直接返回
if(currentCount >= urls.length) return;
const i = currentCount; // 记录当前请求的索引
const url = urls[currentCount++]; // 获取当前请求的URL,并增加当前并发请求数
const data = await fetch(url).then(res => res.json());
results[i] = data; // 将请求结果存储到结果数组中
// 如果结果数组长度等于URL数组长度,说明所有请求已完成,解析Promise
if(results.length === urls.length) {
resolve(results);
}
// 递归调用next函数,处理下一个请求
next();
}
// 并发请求
// 循环发起初始的并发请求,循环次数为Math.min(count, urls.length)
for(let i = 0; i < Math.min(count,urls.length); i++) {
next();
}
})
}
前端引入
const handleRequest = async () => {
//具体的业务场景可以在 urls里自行配置
const urls = Array.from({length: 10}, (_, i) => `/api/concurrency?q=${i}`)
const res = await concurrencyRequests(urls,5)
console.log('result',res)
}
结果返回
限制并发数量
示例图
并发数为5
前一组请求成功之后,再推入下一组请求
代码
/**
* 处理API请求限制的函数
* @param urls - 请求的URL数组
* @param limit - 并发请求的限制数量,默认为5
* @returns 一个Promise,当所有请求完成时解析为结果数组
*/
export async function handleApiLimit(urls: string[], limit = 5) {
let result = [];
let start = 0;
// 初始化结束索引
let end = limit;
let length = urls.length;
// 如果限制数量大于URL数组的长度,则将结束索引设置为URL数组的长度
if (limit > length) {
end = length;
}
// 循环处理URL数组,每次处理limit个URL
while (start < length - 1 urllimiturlurlpromise let apis='[...urls.slice(start,'> fetch(url).then(res => res.json()));
// 等待所有Promise完成,并将结果存储在res数组中
const res = await Promise.all(apis);
// 更新起始索引和结束索引
start = end;
end += limit;
result.push(...res);
}
// 返回结果数组
return result;
}
前端引入
const handleLimitRequest = async () => {
const urls = Array.from({length: 10}, (_, i) => `/api/concurrency?q=${i}`)
const res = await handleApiLimit(urls,5)
console.log('result',res)
}
结果返回