limitReq
实现一个批量请求函数,并限制其并发数,可以通过队列机制或信号量来实现。以下是一个基于 Promise
和队列机制的实现,支持用户设置并发限制。
实现代码
function batchRequest(urls, handler, limit) {
return new Promise((resolve, reject) => {
const total = urls.length; // 总请求数
const results = new Array(total); // 存储请求结果
let completed = 0; // 已完成的请求数
let activeCount = 0; // 当前活动的请求数
let currentIndex = 0; // 当前请求的索引
const next = () => {
// 如果所有请求已完成,结束批量操作
if (completed >= total) {
resolve(results);
return;
}
// 如果达到并发限制或者没有更多请求,直接返回
if (activeCount >= limit || currentIndex >= total) {
return;
}
// 取出下一个请求
const index = currentIndex++;
activeCount++;
// 执行请求
handler(urls[index])
.then((result) => {
results[index] = result; // 保存结果
})
.catch((error) => {
results[index] = error; // 保存错误
})
.finally(() => {
completed++; // 完成数+1
activeCount--; // 活动数-1
next(); // 触发下一个请求
});
// 递归触发更多的并发请求
next();
};
// 启动初始的 `limit` 个并发请求
for (let i = 0; i < limit; i++) {
next();
}
});
}
使用示例
假设我们有一组模拟的请求任务,并希望限制并发数为 3:
// 模拟一个请求函数
function mockRequest(url) {
return new Promise((resolve) => {
const time = Math.random() * 2000; // 随机耗时
setTimeout(() => resolve(`Response from ${url} in ${time.toFixed(0)}ms`), time);
});
}
// 请求地址
const urls = [
"https://example.com/1",
"https://example.com/2",
"https://example.com/3",
"https://example.com/4",
"https://example.com/5",
"https://example.com/6",
];
// 批量请求
batchRequest(urls, mockRequest, 3).then((results) => {
console.log("All requests completed:", results);
});
执行结果
- 同时最多会有 3 个请求并发。
- 请求完成后会按照传入的
urls
顺序返回结果,即使某些请求耗时较长。
输出类似:
All requests completed: [
"Response from https://example.com/1 in 500ms",
"Response from https://example.com/2 in 1000ms",
"Response from https://example.com/3 in 300ms",
"Response from https://example.com/4 in 200ms",
"Response from https://example.com/5 in 700ms",
"Response from https://example.com/6 in 100ms"
]
代码分析
- 并发控制:
- 通过
activeCount
记录当前活跃的请求数,当小于并发限制时,继续启动新的请求。
- 结果存储:
- 通过数组
results
按原始urls
顺序保存请求结果。
- 递归调用:
- 每次请求完成后,通过调用
next()
启动新请求,确保并发请求始终接近限制。
优化建议
- 错误处理:
- 当前实现中,即使某个请求失败,整体流程也会继续,可以根据需求添加全局错误回调。
- 暂停和恢复:
- 可通过增加状态变量(如
paused
)支持暂停和恢复功能。
- 请求超时:
- 可为每个请求添加超时控制,以防止单个请求长时间阻塞队列。
通过这种方式,可以高效地管理并发请求,适用于文件上传、批量任务处理等场景。
使用 class
实现批量请求并限制并发数
class BatchRequest {
constructor(urls, handler, limit) {
this.urls = urls; // 请求的 URL 列表
this.handler = handler; // 请求处理函数
this.limit = limit; // 限制的并发数
this.results = []; // 存储请求结果
this.completed = 0; // 已完成的请求数量
this.activeCount = 0; // 当前活动的请求数量
this.currentIndex = 0; // 当前请求的索引
}
// 发起单个请求
async uploadChunk(url, index) {
try {
const result = await this.handler(url); // 执行请求
this.results[index] = result; // 存储结果
} catch (error) {
this.results[index] = error; // 存储错误信息
} finally {
this.completed++; // 请求完成
this.activeCount--; // 活跃请求数减少
this.next(); // 触发下一个请求
}
}
// 启动并发请求
next() {
// 如果所有请求已完成,返回结果
if (this.completed >= this.urls.length) {
return Promise.resolve(this.results);
}
// 如果并发请求数未达到限制或还有更多请求,继续启动请求
if (this.activeCount < this.limit && this.currentIndex < this.urls.length) {
const index = this.currentIndex++;
this.activeCount++;
this.uploadChunk(this.urls[index], index); // 发起请求
this.next(); // 递归触发更多的并发请求
}
}
// 启动批量请求
start() {
this.next(); // 启动第一个批量请求
}
}
2. 使用示例
假设我们有一组模拟的请求任务,并希望限制并发数为 3:
// 模拟一个请求函数
function mockRequest(url) {
return new Promise((resolve) => {
const time = Math.random() * 2000; // 随机耗时
setTimeout(() => resolve(`Response from ${url} in ${time.toFixed(0)}ms`), time);
});
}
// 请求地址列表
const urls = [
"https://example.com/1",
"https://example.com/2",
"https://example.com/3",
"https://example.com/4",
"https://example.com/5",
"https://example.com/6",
];
// 创建批量请求实例
const batchRequest = new BatchRequest(urls, mockRequest, 3);
// 启动请求
batchRequest.start();
// 获取结果
batchRequest.start().then((results) => {
console.log("All requests completed:", results);
});
3. 代码解析
构造函数 constructor(urls, handler, limit)
:
urls
: 需要请求的 URL 列表。handler
: 请求的处理函数(如mockRequest
)。limit
: 限制的并发请求数。
uploadChunk(url, index)
:
- 处理每个请求,并将结果或错误保存到
results
数组中。 - 请求完成后递减活跃请求数,调用
next()
启动下一个请求。
next()
:
- 控制请求的并发数。
- 当活跃请求数小于
limit
且还有未处理的请求时,会递归调用自己启动新的请求。 - 当所有请求完成时,返回最终的结果。
start()
:
- 启动批量请求,调用
next()
开始控制请求。
4. 优化和扩展
- 错误处理:我们可以扩展错误处理,保证即使某些请求失败,依然继续其他请求的执行。
- 进度监控:可以在每个请求完成后,通过回调函数或事件监听器,监控上传进度。
- 请求超时:为每个请求增加超时机制,防止长时间阻塞。
这个 class
封装了批量请求的逻辑,并支持限制并发数。