文章目录
-
-
- 限制并发数
- 合并请求:减少请求数量
- 缓存策略:避免重复请求
- [防抖 / 节流(针对用户操作触发的请求)](#防抖 / 节流(针对用户操作触发的请求))
- [懒加载 / 分页加载](#懒加载 / 分页加载)
-
前端大规模并发请求,指的是短时间内同时发起数十 / 上百个接口请求,直接并发会导致:浏览器请求阻塞(同域名最多 6-8 个并发)、服务器压力过载、页面卡顿、请求超时 / 失败。
核心解决思路:控制并发数 + 任务队列调度 + 缓存 + 防抖 / 节流 + 合并请求。
限制并发数
浏览器对同一个域名默认限制 6~8 个并发请求,超出的请求会进入排队等待,导致整体请求耗时剧增。
最佳实践:手动控制并发数(比如 3~5 个),用任务队列依次执行,避免浏览器阻塞。
使用p-limit库
p-limit 是一个轻量级 JavaScript 库,主要用于控制 Promise 或异步函数的并发执行数量 ,防止系统因过载而崩溃,安装只需运行 npm install p-limit 命令 。
🚀 基础用法与核心 API
- 创建限制器 :引入库后调用
pLimit函数并传入最大并发数,返回一个限制器函数。- 基础用法:
const limit = pLimit(3)表示最多同时执行 3 个任务 。 - 高级配置:支持传入对象参数,如
{concurrency: 5, rejectOnClear: true},决定清空队列时是否拒绝待处理任务 。
- 基础用法:
- 执行受限任务 :使用限制器包装异步函数,配合
Promise.all等待所有任务完成。- 包装任务:
limit(() => fetchData('user1')),函数不会立即执行,而是进入队列 。 - 批量处理:使用
limit.map(iterable, mapper)可更简洁地处理数组等可迭代对象,自动映射为限流任务 。
- 包装任务:
- 状态监控与控制 :限制器实例暴露了多个属性用于实时监控和调整。
- activeCount:获取当前正在执行的任务数量 。
- pendingCount:获取排队等待的任务数量 。
- **clearQueue()**:清空未执行的队列任务,但不会取消正在运行的任务 。
- 动态调整 :部分版本支持直接修改
limit.concurrency动态调整并发上限 。
🛠️ 常见应用场景
- API 请求限流 :调用外部接口时限制并发数,避免触发速率限制(Rate Limit)或导致服务器压力过大。
- 示例:设置
pLimit(3)确保每秒最多发起 3 个请求,配合limit.map批量获取用户数据 。
- 示例:设置
- 批量文件处理 :读写大量文件时限制同时打开的文件句柄数量,防止资源耗尽。
- 示例:处理上传目录文件时,限制同时读取 5 个文件内容,解析完成后释放资源 。
- 任务优先级控制 :通过创建多个不同并发级别的限制器,实现核心任务优先执行。
- 示例:高优先级任务使用
pLimit(3),后台低优先级任务使用pLimit(1),确保关键业务不受阻塞 。
- 示例:高优先级任务使用
示例代码
javascript
import pLimit from 'p-limit'
initData();
const initData = () => {
console.log('p-limit')
const limit = pLimit(2); // 设置最大并发数量为 8
const input = [ // Limit函数包装各个请求
limit(() => fetchSomething('1')),
limit(() => fetchSomething('2')),
limit(() => fetchSomething('3')),
limit(() => fetchSomething('4')),
limit(() => fetchSomething('5')),
limit(() => fetchSomething('6')),
limit(() => fetchSomething('7')),
limit(() => fetchSomething('8')),
];
// 执行请求
Promise.all(input).then(res => {
console.log(res)
})
};
const fetchSomething = (str) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(str)
resolve(str)
}, 1000)
})
}
效果如图

自己封装方法
请求队列封装
纯 JS 封装,axios/fetch 通用,支持限制最大并发数、任务排队、暂停、清空队列、重试
严格控制并发:同一时间最多 maxLimit 个请求
自动排队:超出并发自动进入等待队列
失败重试:可配置重试次数 + 间隔
灵活管控:暂停 / 恢复 / 清空
无侵入:不改动原有请求逻辑,只包一层函数
RequestQueue.js
javascript
class RequestQueue {
/**
* @param {number} maxLimit 最大并发数
* @param {number} retryTimes 失败重试次数
* @param {number} delay 重试间隔 ms
*/
constructor(maxLimit = 5, retryTimes = 1, delay = 300) {
this.maxLimit = maxLimit;
this.retryTimes = retryTimes;
this.delay = delay;
// 等待执行队列
this.waitQueue = [];
// 正在执行数量
this.runningCount = 0;
// 暂停状态
this.isPause = false;
}
// 添加请求任务
addTask(task) {
return new Promise((resolve, reject) => {
this.waitQueue.push({
task,
resolve,
reject,
retryCount: 0
});
this.run();
});
}
// 执行队列
run() {
if (this.isPause) return;
// 超出并发限制 / 无等待任务 直接返回
if (this.runningCount >= this.maxLimit || this.waitQueue.length === 0) return;
this.runningCount++;
const item = this.waitQueue.shift();
const execute = () => {
item.task()
.then(res => {
item.resolve(res);
this.finishTask();
})
.catch(err => {
// 失败重试
if (item.retryCount < this.retryTimes) {
item.retryCount++;
setTimeout(execute, this.delay);
} else {
item.reject(err);
this.finishTask();
}
});
};
execute();
}
// 任务结束
finishTask() {
this.runningCount--;
// 继续下一个
this.run();
}
// 暂停队列
pause() {
this.isPause = true;
}
// 恢复队列
resume() {
this.isPause = false;
this.run();
}
// 清空等待队列(未执行的全部取消)
clearWaitQueue() {
this.waitQueue = [];
}
}
export default RequestQueue
index.vue
javascript
<template>
<div class="queue">
<div class="title">纯 JS 封装,axios/fetch 通用,支持限制最大并发数、任务排队、暂停、清空队列、重试</div>
<el-button type="primary" @click="addTask">添加任务</el-button>
<el-button type="info" @click="pause">暂停队列</el-button>
<el-button type="success" @click="resume">恢复队列</el-button>
<el-button type="danger" @click="clearWaitQueue">清空等待队列</el-button>
</div>
</template>
<script setup>
import RequestQueue from '@/utils/RequestQueue'
// 模拟异步请求
const fetchSomething = (id) => {
// 关键:必须返回函数,让队列控制什么时候执行
return () => new Promise((resolve) => {
setTimeout(() => {
console.log('执行任务:', id)
resolve(id)
}, 1000)
})
}
// 初始化队列:最大并发3,重试1次,间隔300ms
const requestQueue = new RequestQueue(3, 1, 300)
// 批量添加 10 个任务
const addTask = () => {
const taskList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
taskList.forEach((id) => {
requestQueue.addTask(fetchSomething(id))
.then(res => {
console.log('任务完成:', id, '结果:', res)
})
.catch(err => {
console.error('任务失败:', id, err)
})
})
}
// 队列操作方法
const pause = () => requestQueue.pause()
const resume = () => requestQueue.resume()
const clearWaitQueue = () => requestQueue.clearWaitQueue()
</script>
<style scoped>
.queue {
font-size: 16px;
padding: 20px;
}
.title {
margin-bottom: 20px;
}
</style>
效果如图

合并请求:减少请求数量
能发 1 个请求就绝不发 10 个,从源头降低并发。
后端配合实现 批量接口
前端把多个参数打包成数组,后端一次性返回:
javascript
// 不推荐
GET /api/user/1
GET /api/user/2
GET /api/user/3
// 推荐(批量)
GET /api/user?ids=1,2,3
缓存策略:避免重复请求
把相同的请求缓存起来,下次再请求时,直接从缓存里取。
javascript
const cache = new Map();
axios.interceptors.request.use(config => {
const key = config.url + JSON.stringify(config.params);
if (cache.has(key)) {
return Promise.reject(`已缓存:${key}`); // 取消重复请求
}
cache.set(key, true);
return config;
});
防抖 / 节流(针对用户操作触发的请求)
搜索框:用防抖(debounce),停止输入 500ms 再发请求
滚动加载:用节流(throttle),固定时间只发一次请求
懒加载 / 分页加载
只加载可视区域数据,不一次性请求所有数据