当需要控制前端发起的几百个请求时,直接发送所有请求会导致服务器压力过大,也可能对浏览器和网络造成性能问题。为了高效地管理这些请求,可以采用几种方法来控制请求的并发数。常见的策略包括:
1. 使用队列和批量请求
将所有请求分成多个批次,限制每批次的请求数量,然后按批次依次发送请求。
示例代码:
async function sendRequestsInBatches(requests, batchSize) {
for (let i = 0; i < requests.length; i += batchSize) {
// 获取当前批次的请求
const batch = requests.slice(i, i + batchSize);
// 发送当前批次的请求
await Promise.all(batch.map(req => req()));
console.log(`Batch ${Math.floor(i / batchSize) + 1} complete`);
}
}
// 使用示例
const requests = [
() => fetch('/api/endpoint1'),
() => fetch('/api/endpoint2'),
// 更多请求
];
sendRequestsInBatches(requests, 10); // 每批次最多发送 10 个请求
解释:
- 将所有请求放在一个队列中,每次取出
batchSize个请求并发执行。 Promise.all用于并行执行每批次的请求,await确保当前批次请求完成后才会继续下一批次。
2. 使用限制并发的工具库(如 p-limit 或 Promise Pool)
有一些工具库可以方便地限制并发请求数量,比如 p-limit,它允许你控制并发执行的 Promise 数量。
示例代码(使用 p-limit 库):
import pLimit from 'p-limit';
const limit = pLimit(10); // 限制最大并发为 10
async function sendRequests(requests) {
// 使用 limit 包装每个请求,确保并发数不超过 10
const result = await Promise.all(requests.map(req => limit(() => req())));
console.log('All requests complete');
return result;
}
// 使用示例
const requests = [
() => fetch('/api/endpoint1'),
() => fetch('/api/endpoint2'),
// 更多请求
];
sendRequests(requests);
解释:
- 使用
p-limit库可以限制最大并发数量,limit包装每个请求,使得同时并发的请求数不会超过指定的数量。
3. 使用 async/await 和递归实现串行批次执行
如果需要对请求进行更细粒度的控制,也可以手动通过 async/await 和递归来处理请求。
示例代码:
async function sendRequestsWithLimit(requests, batchSize) {
let index = 0;
// 递归发送请求
async function sendBatch() {
const batch = requests.slice(index, index + batchSize);
await Promise.all(batch.map(req => req()));
index += batch.length;
if (index < requests.length) {
// 如果还有未处理的请求,递归调用
await sendBatch();
}
}
await sendBatch();
console.log('All requests completed');
}
// 使用示例
const requests = [
() => fetch('/api/endpoint1'),
() => fetch('/api/endpoint2'),
// 更多请求
];
sendRequestsWithLimit(requests, 10); // 每次发送 10 个请求
解释:
- 使用递归和
Promise.all来控制并发数量。每次发送的请求数量不超过batchSize,直到所有请求完成。
4. 使用 Web Worker 来分担并发负载
如果请求的任务计算量大,可以考虑使用 Web Worker 来并行处理请求,以避免阻塞主线程。Web Worker 适用于 CPU 密集型任务,但如果只是发送网络请求,通常不需要。
5. 结合 setTimeout 或 setInterval 进行节流
如果想要对请求进行更灵活的控制,也可以结合 setTimeout 或 setInterval 来间隔一定时间发送请求,避免瞬间发起过多请求。
示例代码:
async function sendRequestsWithDelay(requests, delay) {
for (let i = 0; i < requests.length; i++) {
await requests[i]();
console.log(`Request ${i + 1} completed`);
if (i < requests.length - 1) {
// 等待一段时间再发送下一个请求
await new Promise(resolve => setTimeout(resolve, delay));
}
}
console.log('All requests completed');
}
// 使用示例
const requests = [
() => fetch('/api/endpoint1'),
() => fetch('/api/endpoint2'),
// 更多请求
];
sendRequestsWithDelay(requests, 1000); // 每个请求之间间隔 1000ms
解释:
- 通过
setTimeout或new Promise(resolve => setTimeout(resolve, delay))控制每个请求之间的延迟时间,防止过多请求同时发送。
6. 使用 Promise.allSettled 或 Promise.race 来处理请求状态
- 如果你不关心请求是否成功,只关心请求是否完成,可以使用
Promise.allSettled来处理所有请求的完成情况。 - 如果你只关心第一个请求完成,可以使用
Promise.race。
示例代码(使用 Promise.allSettled):
const requests = [
() => fetch('/api/endpoint1'),
() => fetch('/api/endpoint2'),
// 更多请求
];
Promise.allSettled(requests.map(req => req()))
.then(results => {
console.log('All requests completed', results);
});
总结:
- 批量请求:将请求分批次执行,每批次限制并发数量。
p-limit:使用p-limit等库来直接限制并发数。- 递归和
async/await:通过递归或setTimeout控制请求节奏和数量。 - Web Worker:适合计算密集型任务,但不适合普通的网络请求。
这些方法可以有效地控制前端请求的并发量,避免请求过多导致服务器压力过大或浏览器性能问题。