查看: 121|回复: 1

Vue批量请求并发控制实战:4种方案(并发池/分批次/Axios拦截器/p-limit)

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
在Vue项目中,经常需要同时发起几十个请求,如批量查询、批量提交或页面初始化加载多接口数据。如果直接全量并发,会导致网络拥堵、接口超时、浏览器卡顿,甚至触发后端限流。因此,合理控制并发数至关重要。本文提供四种主流并发控制方案,基于Axios,覆盖Vue2和Vue3环境,可直接复制修改使用。

一、并发控制核心逻辑
核心思想:限制同时发起请求的数量,将任务分批次有序执行。关键点包括:
- 控制并发数:根据后端能力和浏览器限制,通常设为4-6个。
- 有序执行:上一批请求完成(无论成功或失败)后,再发起下一批。
- 异常兼容:单个请求失败不应阻塞整体流程,使用Promise.allSettled或逐级处理。

二、4种实战并发控制方案

方案一:并发池控制(最推荐)
封装一个并发池工具,将请求放入队列,限制同时执行数量,某个请求完成后自动从队列中取出下一个执行。

1. 封装并发池工具(通用)
  1. // utils/requestPool.js
  2. import axios from 'axios';
  3. export async function requestPool(requestList, limit = 4, timeout = 10000) {
  4.   const result = [];
  5.   let running = 0;
  6.   let queue = [...requestList];
  7.   
  8.   const runRequest = async () => {
  9.     while (running < limit && queue.length > 0) {
  10.       running++;
  11.       const requestFn = queue.shift();
  12.       const index = requestList.length - queue.length - running;
  13.       try {
  14.         const res = await Promise.race([
  15.           requestFn(),
  16.           new Promise((_, reject) => setTimeout(() => reject(new Error('请求超时')), timeout))
  17.         ]);
  18.         result[index] = { success: true, data: res.data };
  19.       } catch (error) {
  20.         result[index] = { success: false, error: error.message };
  21.       } finally {
  22.         running--;
  23.         await runRequest();
  24.       }
  25.     }
  26.   };
  27.   await runRequest();
  28.   return result;
  29. }
  30. export function createRequestFn(id) {
  31.   return () => axios({ url: `/api/data/${id}`, method: 'get', timeout: 10000 });
  32. }
复制代码
2. 在Vue组件中使用(Vue3示例,Vue2同理)
  1. <template>
  2.   <div>
  3.     <button @click="handleBatchRequest">发起30个并发请求</button>
  4.     <div>成功:{{ successCount }} 个 | 失败:{{ failCount }} 个</div>
  5.   </div>
  6. </template>
  7. <script setup>
  8. import { ref } from 'vue';
  9. import { requestPool, createRequestFn } from '@/utils/requestPool';
  10. const successCount = ref(0);
  11. const failCount = ref(0);
  12. const handleBatchRequest = async () => {
  13.   const requestList = Array.from({ length: 30 }, (_, i) => createRequestFn(i + 1));
  14.   const results = await requestPool(requestList, 5);
  15.   successCount.value = results.filter(item => item.success).length;
  16.   failCount.value = results.filter(item => !item.success).length;
  17. };
  18. </script>
复制代码
优势:灵活可控,可自定义并发数和超时;单个请求失败不影响整体;结果顺序与发起顺序一致;代码复用性强。适合所有批量请求场景。

方案二:分批次请求(适合对顺序要求高的场景)
将请求分成若干批次,每批固定数量(如5个),当前批次所有请求完成后才发下一批。实现简单,适合下一批依赖上一批结果的场景。

1. 封装分批次请求工具
  1. // utils/batchRequest.js
  2. import axios from 'axios';
  3. export async function batchRequest(requestList, batchSize = 5) {
  4.   const result = [];
  5.   const totalBatch = Math.ceil(requestList.length / batchSize);
  6.   for (let i = 0; i < totalBatch; i++) {
  7.     const currentBatch = requestList.slice(i * batchSize, (i + 1) * batchSize);
  8.     const batchResult = await Promise.allSettled(
  9.       currentBatch.map(id =>
  10.         axios({ url: `/api/data/${id}`, method: 'get', timeout: 10000 })
  11.           .then(res => ({ success: true, data: res.data }))
  12.           .catch(err => ({ success: false, error: err.message }))
  13.       )
  14.     );
  15.     result.push(...batchResult);
  16.   }
  17.   return result;
  18. }
复制代码
2. 在组件中使用
  1. <script setup>
  2. import { ref } from 'vue';
  3. import { batchRequest } from '@/utils/batchRequest';
  4. const successCount = ref(0);
  5. const failCount = ref(0);
  6. const handleBatchRequest = async () => {
  7.   const requestList = Array.from({ length: 30 }, (_, i) => i + 1);
  8.   const results = await batchRequest(requestList, 5);
  9.   successCount.value = results.filter(item => item.success).length;
  10.   failCount.value = results.filter(item => !item.success).length;
  11. };
  12. </script>
复制代码
优势:实现简单,批次清晰,顺序可控。适合分批提交表单、依赖上一批结果的场景。

方案三:Axios拦截器控制并发(全局管控)
在Axios请求/响应拦截器中维护一个计数器,达到限制时将后续请求存入队列,等完成后再取出执行。无需在组件中单独处理。

1. 全局配置Axios并发控制
  1. // utils/axiosConfig.js
  2. import axios from 'axios';
  3. const CONCURRENT_LIMIT = 4;
  4. let requestCount = 0;
  5. const requestQueue = [];
  6. const service = axios.create({
  7.   baseURL: import.meta.env.VITE_API_BASE_URL,
  8.   timeout: 10000
  9. });
  10. service.interceptors.request.use(
  11.   config => new Promise(resolve => {
  12.     if (requestCount < CONCURRENT_LIMIT) {
  13.       requestCount++;
  14.       resolve(config);
  15.     } else {
  16.       requestQueue.push(resolve);
  17.     }
  18.   }),
  19.   error => Promise.reject(error)
  20. );
  21. service.interceptors.response.use(
  22.   response => {
  23.     requestCount--;
  24.     if (requestQueue.length > 0) {
  25.       const resolve = requestQueue.shift();
  26.       requestCount++;
  27.       resolve(service.defaults);
  28.     }
  29.     return response;
  30.   },
  31.   error => {
  32.     requestCount--;
  33.     if (requestQueue.length > 0) {
  34.       const resolve = requestQueue.shift();
  35.       requestCount++;
  36.       resolve(service.defaults);
  37.     }
  38.     return Promise.reject(error);
  39.   }
  40. );
  41. export default service;
复制代码
2. 在组件中使用
  1. <script setup>
  2. import { ref } from 'vue';
  3. import request from '@/utils/axiosConfig';
  4. const successCount = ref(0);
  5. const failCount = ref(0);
  6. const handleBatchRequest = async () => {
  7.   const requestList = [];
  8.   for (let i = 1; i <= 30; i++) {
  9.     requestList.push(
  10.       request({ url: `/api/data/${i}`, method: 'get' })
  11.         .then(res => ({ success: true, data: res.data }))
  12.         .catch(err => ({ success: false, error: err.message }))
  13.     );
  14.   }
  15.   const results = await Promise.allSettled(requestList);
  16.   successCount.value = results.filter(item => item.success).length;
  17.   failCount.value = results.filter(item => !item.success).length;
  18. };
  19. </script>
复制代码
优势:全局统一管控,侵入性低。适合简单、无需个性化配置的场景。注意:此方案无法保证请求结果顺序。

方案四:使用第三方库 p-limit
p-limit 是一个轻量级并发控制库,无需自己封装工具,支持动态调整并发数。

安装:npm install p-limit --save
使用示例(组件内):
  1. <script setup>
  2. import { ref } from 'vue';
  3. import axios from 'axios';
  4. import pLimit from 'p-limit';
  5. const limit = pLimit(4);
  6. const successCount = ref(0);
  7. const failCount = ref(0);
  8. const handleBatchRequest = async () => {
  9.   const requestList = [];
  10.   for (let i = 1; i <= 30; i++) {
  11.     requestList.push(
  12.       limit(() =>
  13.         axios({ url: `/api/data/${i}`, method: 'get', timeout: 10000 })
  14.           .then(res => ({ success: true, data: res.data }))
  15.           .catch(err => ({ success: false, error: err.message }))
  16.       )
  17.     );
  18.   }
  19.   const results = await Promise.allSettled(requestList);
  20.   successCount.value = results.filter(item => item.success).length;
  21.   failCount.value = results.filter(item => !item.success).length;
  22. };
  23. </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中处理批量请求并发控制的完整实战方案。
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: Vue批量请求并发控制实战:4种方案(并发池/分批次/Axios拦截器/p-limit)

楼主好文!最近正好在优化批量查询接口,这四种方案总结得很到位。方案一的并发池控制思路清晰,用队列和控制running数量实现并发限制,代码量少且可复用,确实是最推荐的做法。不过注意到代码里 `result` 用了 `const` 但后面在循环里直接 `result = { success: true ... }` 会报错,应该改成 `let result = []` 并在 `result` 赋值,或者用 push?另外超时处理单独用 `Promise.race` 的方式也很实用。方案二分批次请求逻辑简单直接,适合需要按顺序处理或有依赖关系的场景,比如先获取用户列表再批量获取详情。期待你后续更新方案三(Axios拦截器)和方案四(p-limit)的实现细节,特别是拦截器如何统一管理并发数量,很感兴趣。感谢分享!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-12 18:00 , Processed in 0.032932 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部