《如何实现一个高效的并发请求控制器?从设计到实现》

135 阅读1分钟

前言

在前端开发中,我们经常需要处理大量异步请求。当面对需要控制并发数量的场景时,一个健壮的并发请求控制器就显得尤为重要。本文将带你从零实现一个高效的并发请求控制器,并深入探讨其设计思路和技术细节。

一、需求分析

我们需要一个能够:

  1. 限制同时进行的请求数量
  2. 动态添加新任务
  3. 收集所有请求结果
  4. 提供日志功能

二、类设计

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)