在Vue项目中,经常需要同时发起几十个请求,如批量查询、批量提交或页面初始化加载多接口数据。如果直接全量并发,会导致网络拥堵、接口超时、浏览器卡顿,甚至触发后端限流。因此,合理控制并发数至关重要。本文提供四种主流并发控制方案,基于Axios,覆盖Vue2和Vue3环境,可直接复制修改使用。
一、并发控制核心逻辑
核心思想:限制同时发起请求的数量,将任务分批次有序执行。关键点包括:
- 控制并发数:根据后端能力和浏览器限制,通常设为4-6个。
- 有序执行:上一批请求完成(无论成功或失败)后,再发起下一批。
- 异常兼容:单个请求失败不应阻塞整体流程,使用Promise.allSettled或逐级处理。
二、4种实战并发控制方案
方案一:并发池控制(最推荐)
封装一个并发池工具,将请求放入队列,限制同时执行数量,某个请求完成后自动从队列中取出下一个执行。
1. 封装并发池工具(通用)- // 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 () => {
- 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;
- }
- 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>成功:{{ 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);
- 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>
复制代码 优势:灵活可控,可自定义并发数和超时;单个请求失败不影响整体;结果顺序与发起顺序一致;代码复用性强。适合所有批量请求场景。
方案二:分批次请求(适合对顺序要求高的场景)
将请求分成若干批次,每批固定数量(如5个),当前批次所有请求完成后才发下一批。实现简单,适合下一批依赖上一批结果的场景。
1. 封装分批次请求工具- // utils/batchRequest.js
- import axios from 'axios';
- 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. 在组件中使用- <script setup>
- import { ref } from 'vue';
- import { batchRequest } from '@/utils/batchRequest';
- const successCount = ref(0);
- const failCount = ref(0);
- const handleBatchRequest = async () => {
- const requestList = Array.from({ length: 30 }, (_, i) => i + 1);
- 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并发控制- // utils/axiosConfig.js
- import axios from 'axios';
- const CONCURRENT_LIMIT = 4;
- let requestCount = 0;
- const requestQueue = [];
- const service = axios.create({
- baseURL: import.meta.env.VITE_API_BASE_URL,
- timeout: 10000
- });
- service.interceptors.request.use(
- config => new Promise(resolve => {
- if (requestCount < CONCURRENT_LIMIT) {
- requestCount++;
- resolve(config);
- } else {
- requestQueue.push(resolve);
- }
- }),
- error => Promise.reject(error)
- );
- service.interceptors.response.use(
- response => {
- requestCount--;
- if (requestQueue.length > 0) {
- const resolve = requestQueue.shift();
- requestCount++;
- resolve(service.defaults);
- }
- return response;
- },
- error => {
- requestCount--;
- if (requestQueue.length > 0) {
- const resolve = requestQueue.shift();
- requestCount++;
- resolve(service.defaults);
- }
- return Promise.reject(error);
- }
- );
- export default service;
复制代码 2. 在组件中使用- <script setup>
- import { ref } from 'vue';
- import request from '@/utils/axiosConfig';
- const successCount = ref(0);
- const failCount = ref(0);
- 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 是一个轻量级并发控制库,无需自己封装工具,支持动态调整并发数。
安装:npm install p-limit --save
使用示例(组件内):- <script setup>
- import { ref } from 'vue';
- import axios from 'axios';
- import pLimit from 'p-limit';
- const limit = pLimit(4);
- const successCount = ref(0);
- const failCount = ref(0);
- const handleBatchRequest = async () => {
- const requestList = [];
- for (let i = 1; i <= 30; i++) {
- 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,修改baseURL时需注意。
- 第三方库如p-limit在所有Vue版本中均可正常使用。
四、避坑指南
- 并发数设置:建议4-6个,过大会导致网络拥堵或后端限流。
- 超时控制:每个请求必须设置超时时间,避免单个请求卡死整体流程。
- 失败处理:使用Promise.allSettled替代Promise.all,防止一个失败中断所有。
- 请求顺序:并发池和分批次方案可保证顺序;Axios拦截器方案不保证,需额外处理。
- 内存占用:大量请求时避免存储全部结果,可边请求边渲染减少内存。
五、总结
- 并发池控制:最推荐,灵活可控,适配所有场景。
- 分批次请求:适合对顺序依赖强的场景。
- Axios拦截器:适合全局统一管控的简单场景。
- p-limit:适合复杂需求,避免自建轮子。
实际开发中建议优先使用并发池控制方案。将本文中的代码复制到项目,修改请求地址和参数即可快速落地。以上就是在Vue中处理批量请求并发控制的完整实战方案。 |