在前端开发中,如果需要处理大量的并发请求,可以使用一些技术来控制并发数量,以避免过多的请求对服务器造成压力或导致浏览器 性能 问题。以下是几种常见的方法:
方法一:使用 Promise.all
和批量处理 (不受控并发)
方法:将请求分成多个批次,每个批次使用 Promise.all 进行并发处理。
const fetchData = async (urls) => {
const batchSize = 5; // 每批次请求数量
const results = []; // 用于存储所有请求结果
for (let i = 0; i < urls.length; i += batchSize) {
// 每次取出 batchSize 个 URL,作为一个批次
const batch = urls.slice(i, i + batchSize);
// 并行执行当前批次的请求,并等待所有请求完成
const batchResults = await Promise.all(
batch.map(url => fetch(url).then(res => res.json()))
);
// 将当前批次的结果合并到 results 数组
results.push(...batchResults);
}
return results; // 返回所有批次请求的结果
};
// 示例
const urls = [
'https://api.example.com/data1',
'https://api.example.com/data2',
// ... 其他 URL
];
fetchData(urls).then(results => {
console.log(results);
});
优点:
- 执行最快,所有请求同时进行。
缺点:
- 可能导致请求过载,API 可能有并发限制(如 10 个/秒)。
- 占用大量资源,影响页面性能。
方法二:使用 Promise.race() 控制并发 (最优解,推荐)
问题:有 8 个图片资源的 url,已经存储在数组 urls 中,而且已经有一个函数 function loadImg,输入一个 url 链接,返回一个 Promise,该 Promise 在图片下载完成的时候 resolve,下载失败则 reject。但是我们要求,任意时刻,同时下载的链接数量不可以超过 3 个。
请写一段代码实现这个需求,要求尽可能快速地将所有图片下载完成。
下图这样的排队和并发请求的场景基本类似,窗口只有三个,人超过三个之后,后面的人只能排队了。
var urls = [
"https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg",
"https://www.kkkk1000.com/images/getImgData/gray.gif",
"https://www.kkkk1000.com/images/getImgData/Particle.gif",
"https://www.kkkk1000.com/images/getImgData/arithmetic.png",
"https://www.kkkk1000.com/images/getImgData/arithmetic2.gif",
"https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg",
"https://www.kkkk1000.com/images/getImgData/arithmetic.gif",
"https://www.kkkk1000.com/images/wxQrCode2.png",
];
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function () {
resolve(url); // 图片加载成功,返回 URL
};
img.onerror = reject; // 加载失败时调用 reject
img.src = url; // 触发图片加载
});
}
async function limitLoad(urls, handler, limit) {
// 存储所有加载任务
const promises = [];
const queue = urls.splice(0, limit).map((url, index) => {
const _p = handler(url); // 启动前 limit 个任务
promises.push(_p); // 记录该任务
return _p.then((res) => {
// 任务完成后返回任务索引和结果
return [index, res];
});
});
for (const item of urls) {
const [index] = await Promise.race(queue);
const _p = handler(item);
promises.push(_p);
queue[index] = _p.then((res) => {
return [index, res];
});
}
return Promise.allSettled(promises);
}
limitLoad(urls, loadImg, 3).then((res) => console.log(res));
优点:
- 动态控制并发,确保始终有 limit 个任务在执行,不会阻塞队列。
- 更加高效,适用于大量请求的场景。
方法三:使用 p-limit 库(推荐)
p-limit 是一个用于限制并发数的库,可以更方便地控制并发请求数量。
安装 p-limit
npm install p-limit
使用 p-limit
import pLimit from 'p-limit';
const limit = pLimit(5); // 设置并发限制为 5
const fetchData = async (urls) => {
const promises = urls.map(url => limit(() => fetch(url).then(res => res.json())));
const results = await Promise.all(promises);
return results;
};
// 示例
const urls = [
'https://api.example.com/data1',
'https://api.example.com/data2',
// ... 其他 URL
];
fetchData(urls).then(results => {
console.log(results);
});
优点:
p-limit 自动控制并发,代码更简洁。
可扩展性强,适合大规模并发请求。
如何选择最优方案?
- 如果请求量小(<10),可以使用 Promise.all(),但不推荐大量请求时使用。
- 如果 API 有并发限制,建议使用 批量执行(方案 1)。
- 如果请求量大(几十到上百),最优解是:Promise.race() 任务队列 或 p-limit 动态控制并发。
在 Vue3 + JS 中控制并发请求的最佳方式是使用 p-limit 或手写 asyncPool()。相比 Promise.all(),它能有效避免服务器压力过大,提高请求效率。