在处理大量并发请求时,为了避免服务器过载或客户端性能下降,可以使用并发控制技术。以下是几种常见的方式:
1. 使用 Promise.allSettled
和分批处理
将请求分成小批次,并逐批执行,每一批的大小由并发限制决定:
async function fetchWithConcurrency(urls, maxConcurrency) {
const results = [];
while (urls.length > 0) {
// 取出一批URL
const batch = urls.splice(0, maxConcurrency);
// 使用 Promise.allSettled 执行一批请求
const batchResults = await Promise.allSettled(
batch.map(url => fetch(url))
);
results.push(...batchResults);
}
return results;
}
const urls = Array(100).fill('https://example.com/api');
fetchWithConcurrency(urls, 5).then(results => {
console.log(results); // 输出每个请求的状态与结果
});
2. 使用队列方式控制并发
创建一个队列控制器,通过限制同时运行的 Promise 数量来管理并发:
class PromiseQueue {
constructor(maxConcurrency) {
this.maxConcurrency = maxConcurrency;
this.running = 0;
this.queue = [];
}
enqueue(task) {
return new Promise((resolve, reject) => {
this.queue.push(() => task().then(resolve, reject));
this.runNext();
});
}
runNext() {
if (this.running >= this.maxConcurrency || this.queue.length === 0) {
return;
}
const task = this.queue.shift();
this.running++;
task().finally(() => {
this.running--;
this.runNext();
});
}
}
// 使用队列
const queue = new PromiseQueue(5); // 并发数为 5
const urls = Array(100).fill('https://example.com/api');
Promise.all(
urls.map(url => queue.enqueue(() => fetch(url)))
).then(results => {
console.log(results);
});
3. 使用第三方库
可以使用现成的库如 p-limit 或 promise-pool 来管理并发。
示例:使用 p-limit
const pLimit = require('p-limit');
const limit = pLimit(5); // 最大并发数 5
const urls = Array(100).fill('https://example.com/api');
const tasks = urls.map(url => limit(() => fetch(url)));
Promise.all(tasks).then(results => {
console.log(results);
});
示例:使用 promise-pool
const { promisePool } = require('promise-pool');
const urls = Array(100).fill('https://example.com/api');
async function fetchUrl(url) {
const response = await fetch(url);
return response.json();
}
promisePool({ items: urls, concurrency: 5, task: fetchUrl })
.then(results => {
console.log(results);
});
4. 浏览器专用:AbortController
限制超时
结合超时机制,使用 AbortController
提前中止请求,防止某些请求过长拖慢整个流程:
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const signal = controller.signal;
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, { signal });
return await response.json();
} catch (err) {
return { error: 'Timeout or network error' };
} finally {
clearTimeout(timeoutId);
}
}
// 并发控制逻辑同上
选择建议:
- 任务数量多,但单任务时间较短: 分批或
PromiseQueue
更适合。 - 任务数量多且复杂: 使用
p-limit
等库实现并发控制。 - 实时性要求高: 考虑
AbortController
或合理设置超时策略。