Vue并发控制|几十个请求高效管控(实战方案+可运行代码)
在Vue项目开发中,经常会遇到需要同时发起几十个请求的场景(如批量数据查询、批量提交、页面初始化加载多接口数据)。若直接同时发起所有请求,会导致网络拥堵、接口超时、浏览器卡顿,甚至触发后端接口限流,影响用户体验和系统稳定性。本文结合Vue2/Vue3实战,提供4种主流并发控制方案,覆盖不同场景,可直接落地使用,轻松管控几十个请求的并发逻辑。
一、并发控制核心逻辑
并发控制的核心是:限制同一时间发起的请求数量,将几十个请求分批次、有序执行,避免一次性占用过多网络资源。核心要点的2个:
- 控制并发数:根据后端接口承载能力和浏览器限制,合理设置并发数(通常4-6个,过多易拥堵,过少效率低);
- 有序执行:分批次发起请求,上一批请求完成(成功/失败)后,再发起下一批,确保请求有序且不拥堵;
- 异常兼容:处理单个请求失败、超时场景,避免单个请求失败导致整个并发流程中断。
以下方案均适配Vue2/Vue3,基于Axios请求库(Vue项目主流请求工具),可直接复制到项目中修改使用。
二、4种实战并发控制方案(按推荐度排序)
方案一:并发池控制(最推荐,灵活高效,适配所有场景)
核心思路:封装一个并发池工具,将所有请求放入队列,限制同时执行的请求数量,当某个请求完成后,自动从队列中取出下一个请求执行,循环直至所有请求完成。该方案灵活可控,可处理成功/失败回调、超时控制,是几十个请求并发管控的最优选择。
1. 封装并发池工具(Vue2/Vue3通用)
// utils/requestPool.js
import axios from 'axios';
// 极简版并发池(核心功能:限制并发、处理超时、返回结果)
export async function requestPool(requestList, limit = 4, timeout = 10000) {
const result = [];
let running = 0;
let queue = [...requestList];
const runRequest = async () => {
if (queue.length === 0 && running === 0) return result;
while (running < limit && queue.length > 0) {
running++;
const requestFn = queue.shift();
const index = requestList.length - queue.length - running;
try {
const res = await Promise.race([
requestFn(),
new Promise((_, reject) => setTimeout(() => reject(new Error('请求超时')), timeout))
]);
result[index] = { success: true, data: res.data };
} catch (error) {
result[index] = { success: false, error: error.message };
} finally {
running--;
await runRequest();
}
}
};
await runRequest();
return result;
}
// 极简请求函数(按需修改url和参数)
export function createRequestFn(id) {
return () => axios({ url: `/api/data/${id}`, method: 'get', timeout: 10000 });
}
2. Vue组件中使用(Vue3示例,Vue2可直接适配)
<template>
<div>
<button @click="handleBatchRequest">发起30个并发请求</button>
<div class="result">成功:{{ successCount }} 个 | 失败:{{ failCount }} 个</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { requestPool, createRequestFn } from '@/utils/requestPool';
const successCount = ref(0);
const failCount = ref(0);
// 极简使用示例(30个请求,并发数5)
const handleBatchRequest = async () => {
const requestList = Array.from({ length: 30 }, (_, i) => createRequestFn(i + 1));
const results = await requestPool(requestList, 5);
successCount.value = results.filter(item => item.success).length;
failCount.value = results.filter(item => !item.success).length;
};
</script>
<style scoped>
.result { margin-top: 20px; font-size: 14px; color: #333; }
</style>
方案一优势与适配场景
- 优势:灵活可控,可自定义并发数、超时时间;单个请求失败不影响整体流程;请求结果顺序与发起顺序一致;代码复用性强,可全局调用。
- 适配场景:几十个请求的批量查询、批量提交、页面初始化多接口加载等所有场景(最推荐)。
方案二:分批次请求(简单易实现,适合对顺序要求高的场景)
核心思路:将几十个请求分成若干批次,每批次发起固定数量的请求(如每批5个),等待当前批次所有请求完成后,再发起下一批。实现简单,无需复杂封装,适合对请求顺序有严格要求的场景(如下一批请求依赖上一批请求结果)。
1. 封装分批次请求工具(Vue2/Vue3通用)
// utils/batchRequest.js
import axios from 'axios';
/**
* 分批次请求控制
* @param {Array} requestList - 请求列表(每个元素是请求参数,如id)
* @param {Number} batchSize - 每批请求数量(默认5个)
* @returns {Promise} - 所有请求结果数组
*/
export async function batchRequest(requestList, batchSize = 5) {
const result = [];
// 计算总批次
const totalBatch = Math.ceil(requestList.length / batchSize);
// 循环发起每批次请求
for (let i = 0; i < totalBatch; i++) {
// 截取当前批次的请求参数
const currentBatch = requestList.slice(i * batchSize, (i + 1) * batchSize);
// 发起当前批次的所有请求(并行)
const batchResult = await Promise.allSettled(
currentBatch.map(id =>
axios({
url: `/api/data/${id}`,
method: 'get',
timeout: 10000
}).then(res => ({ success: true, data: res.data }))
.catch(err => ({ success: false, error: err.message }))
)
);
// 将当前批次结果存入总结果
result.push(...batchResult);
}
return result;
}
2. Vue组件中使用
<script setup>
import { ref } from 'vue';
import { batchRequest } from '@/utils/batchRequest';
const successCount = ref(0);
const failCount = ref(0);
const handleBatchRequest = async () => {
// 生成30个请求参数(如id数组)
const requestList = Array.from({ length: 30 }, (_, i) => i + 1);
// 分批次请求,每批5个
const results = await batchRequest(requestList, 5);
// 处理结果
successCount.value = results.filter(item => item.success).length;
failCount.value = results.filter(item => !item.success).length;
};
</script>
方案二优势与适配场景
- 优势:实现简单,无需复杂封装;请求批次清晰,顺序可控;适合下一批请求依赖上一批结果的场景。
- 适配场景:对请求顺序有要求、批量提交且需分批校验的场景(如分批提交表单数据,上一批通过再提交下一批)。
方案三:Axios拦截器控制并发(全局管控,适合简单场景)
核心思路:通过Axios的请求拦截器和响应拦截器,维护一个“正在执行的请求”计数器,当计数器达到并发限制时,将后续请求存入队列,等待正在执行的请求完成后,再依次发起。适合简单场景,无需在组件中单独处理,全局统一管控。
1. 全局配置Axios并发控制(Vue2/Vue3通用)
// utils/axiosConfig.js
import axios from 'axios';
// 并发限制数
const CONCURRENT_LIMIT = 4;
// 正在执行的请求计数器
let requestCount = 0;
// 请求队列
const requestQueue = [];
// 创建Axios实例
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000
});
// 请求拦截器:控制并发
service.interceptors.request.use(
config => {
return new Promise(resolve => {
// 若当前请求数未达限制,直接发起请求
if (requestCount < CONCURRENT_LIMIT) {
requestCount++;
resolve(config);
} else {
// 达到限制,存入请求队列
requestQueue.push(resolve);
}
});
},
error => {
return Promise.reject(error);
}
);
// 响应拦截器:请求完成后,从队列中取出下一个请求
service.interceptors.response.use(
response => {
// 请求完成,计数器减1
requestCount--;
// 若队列中有请求,取出并执行
if (requestQueue.length > 0) {
const resolve = requestQueue.shift();
requestCount++;
resolve(service.defaults);
}
return response;
},
error => {
// 失败也需计数器减1,避免队列卡住
requestCount--;
if (requestQueue.length > 0) {
const resolve = requestQueue.shift();
requestCount++;
resolve(service.defaults);
}
return Promise.reject(error);
}
);
export default service;
2. Vue组件中使用
<script setup>
import { ref } from 'vue';
import request from '@/utils/axiosConfig';
const successCount = ref(0);
const failCount = ref(0);
// 直接发起30个请求,Axios拦截器自动控制并发
const handleBatchRequest = async () => {
const requestList = [];
for (let i = 1; i <= 30; i++) {
requestList.push(
request({
url: `/api/data/${i}`,
method: 'get'
}).then(res => ({ success: true, data: res.data }))
.catch(err => ({ success: false, error: err.message }))
);
}
// 等待所有请求完成
const results = await Promise.allSettled(requestList);
successCount.value = results.filter(item => item.success).length;
failCount.value = results.filter(item => !item.success).length;
};
</script>
方案三优势与适配场景
- 优势:全局统一管控,组件中无需单独处理并发逻辑;侵入性低,原有请求代码无需修改;实现简单,适合快速落地。
- 适配场景:项目中所有批量请求需统一控制并发、无需个性化并发配置的简单场景。
方案四:使用第三方库(高效快捷,适合复杂场景)
核心思路:借助成熟的第三方库(如p-limit),快速实现并发控制,无需自己封装工具,适合复杂场景(如并发数动态调整、请求优先级控制)。p-limit轻量、易用,是前端并发控制的常用库。
1. 安装与使用(Vue2/Vue3通用)
// 1. 安装依赖
// npm install p-limit --save
// 2. 组件中使用
<script setup>
import { ref } from 'vue';
import axios from 'axios';
import pLimit from 'p-limit';
// 设置并发限制数为4
const limit = pLimit(4);
const successCount = ref(0);
const failCount = ref(0);
const handleBatchRequest = async () => {
// 生成30个请求函数,并用p-limit包装
const requestList = [];
for (let i = 1; i <= 30; i++) {
// 用limit包装请求函数,限制并发
requestList.push(
limit(() =>
axios({
url: `/api/data/${i}`,
method: 'get',
timeout: 10000
}).then(res => ({ success: true, data: res.data }))
.catch(err => ({ success: false, error: err.message }))
)
);
}
// 等待所有请求完成
const results = await Promise.allSettled(requestList);
successCount.value = results.filter(item => item.success).length;
failCount.value = results.filter(item => !item.success).length;
};
</script>
方案四优势与适配场景
- 优势:无需自己封装,高效快捷;支持动态调整并发数、请求优先级;成熟稳定,适配复杂并发场景。
- 适配场景:复杂并发需求(如动态调整并发数、设置请求优先级)、不想自己封装工具的场景。
三、Vue2与Vue3适配差异(关键注意点)
- 请求工具:两者均使用Axios,配置方式完全一致,无差异;
- 组件写法:Vue3使用组合式API(setup语法),Vue2使用选项式API(methods中编写逻辑),核心并发控制逻辑完全一致;
- 环境变量:Vue3使用import.meta.env,Vue2使用process.env,修改Axios baseURL时需注意适配;
- 第三方库:p-limit等库适配所有Vue版本,无差异。
四、避坑指南(高频问题)
- 并发数设置:避免设置过大(如超过10个),否则会导致网络拥堵、后端限流;建议设置4-6个,根据后端接口承载能力调整;
- 超时控制:必须为单个请求设置超时时间,避免某个请求长时间挂起,导致整个并发流程卡住;
- 失败处理:使用Promise.allSettled(而非Promise.all),避免单个请求失败导致整个并发流程中断;
- 请求顺序:并发池和分批次方案可保证请求结果顺序与发起顺序一致,Axios拦截器方案无法保证顺序(需额外处理);
- 内存占用:几十个请求并发时,避免存储过多请求结果,可按需处理结果(如边请求边渲染),减少内存占用。
五、总结
针对Vue中几十个请求的并发控制,4种方案各有适配场景,可根据项目需求灵活选择:
- 并发池控制:最推荐,灵活可控、适配所有场景,兼顾易用性和扩展性;
- 分批次请求:适合对请求顺序有要求、下一批依赖上一批结果的场景;
- Axios拦截器:适合全局统一管控、无需个性化配置的简单场景;
- 第三方库:适合复杂场景、不想自己封装工具的场景。
实际开发中,建议优先使用“并发池控制”方案,既能灵活控制并发数、处理异常,又能保证请求顺序,可直接复制本文代码,修改请求地址和参数即可快速落地,轻松解决几十个请求的并发难题。