前言
在前端开发中,我们经常需要处理大量异步请求。当面对需要控制并发数量的场景时,一个健壮的并发请求控制器就显得尤为重要。本文将带你从零实现一个高效的并发请求控制器,并深入探讨其设计思路和技术细节。
一、需求分析
我们需要一个能够:
- 限制同时进行的请求数量
- 动态添加新任务
- 收集所有请求结果
- 提供日志功能
二、类设计
export default class ConcurrencyRequest {
#maxCount // 最大并发数
#requestList // 待执行任务队列
#log // 日志开关
#responseList = {} // 结果收集
constructor(maxCount, requestList, log) {
this.#maxCount = maxCount
this.#requestList = requestList
this.#log = log
}
// 添加新任务
add(task) {
this.#requestList.push(task)
}
// 执行单个任务
runTask(task) {
task()
.then((result) => {
this.#responseList[task.name] = {
name: task.name,
result: result,
error: null
}
})
.catch((err) => {
this.#responseList[task.name] = {
name: task.name,
result: null,
error: err
}
})
.finally(() => {
this.#log && console.log(this.#responseList[task.name])
this.#maxCount += 1
this.scheduleTasks()
})
}
// 调度任务
scheduleTasks() {
const mixCount = Math.min(this.#maxCount, this.#requestList.length)
for(let i = 0; i < mixCount; i++) {
const task = this.#requestList.shift()
this.#maxCount -= 1
this.runTask(task)
}
}
}
三、核心实现解析
1. 私有字段的使用
使用 # 前缀定义私有字段,这是ES2022的新特性,相比以前的 _ 约定更安全可靠。
2. 任务调度机制
scheduleTasks 方法是核心:
- 计算当前可运行的任务数量
- 从队列中取出任务执行
- 动态调整可用并发数
3. 结果收集
使用对象存储结果,以任务名称为key,便于快速查找:
{
"task1": { name: "task1", result: "...", error: null },
"task2": { name: "task2", result: null, error: Error }
}
四、使用示例
function request1(){
return fetch('/api/1').then(res => res)
}
function request2(){
return fetch('/api/2').then(res => res)
}
const controller = new ConcurrencyRequest(2, [request1, request2], true)
controller.scheduleTasks()
// 动态添加任务
function request3(){
return fetch('/api/3').then(res => res)
}
controller.add(request3)