前端如何优雅地实现一个“请求队列”,避免服务器被卡死?

发布于:2025-07-02 ⋅ 阅读:(23) ⋅ 点赞:(0)

有这样一些场景:

  • 页面一加载,需要同时发 10 个请求,结果页面卡住,服务器也快崩了。
  • 用户可以批量操作,一次点击触发了几十个上传文件的请求,浏览器直接转圈圈。

当后端处理不过来时,前端一股脑地把请求全发过去,只会让情况更糟。

核心思想就一句话:不要一次性把所有请求都发出去,让它们排队,一个一个来,或者一小批一小批来。

这就好比超市结账,只有一个收银台,却来了100个顾客。最好的办法就是让他们排队,而不是一拥而上。我们的“请求队列”就是这个“排队管理员”。

直接上代码:一个即插即用的请求队列

不用复杂的分析,直接复制下面的 RequestPool 类到我们的项目里。它非常小巧,只有不到 40 行代码。

/**
 * 一个简单的请求池/请求队列,用于控制并发
 * @example
 * const pool = new RequestPool(3); // 限制并发数为 3
 * pool.add(() => myFetch('/api/1'));
 * pool.add(() => myFetch('/api/2'));
 */
class RequestPool {
 /**
   * @param {number} limit - 并发限制数
   */
 constructor(limit = 3) {
    this.limit = limit; // 并发限制数
    this.queue = [];    // 等待的请求队列
    this.running = 0;   // 当前正在运行的请求数
  }

 /**
   * 添加一个请求到池中
   * @param {Function} requestFn - 一个返回 Promise 的函数
   * @returns {Promise}
   */
 add(requestFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ requestFn, resolve, reject });
      this._run(); // 每次添加后,都尝试运行
    });
  }

 _run() {
    // 只有当 正在运行的请求数 < 限制数 且 队列中有等待的请求时,才执行
    while (this.running < this.limit && this.queue.length > 0) {
      const { requestFn, resolve, reject } = this.queue.shift(); // 取出队首的任务
      this.running++;

      requestFn()
        .then(resolve)
        .catch(reject)
        .finally(() => {
          this.running--; // 请求完成,空出一个位置
          this._run();   // 尝试运行下一个
        });
    }
  }
}

如何使用?三步搞定!

假设你有一个请求函数 mockApi,它会模拟一个比较慢的接口

// 1.模拟一个慢的接口
function MockApi(id: number) {
  const delay = Math.random() * 1000 + 500;
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log(`[${id}] 请求完成`);
      resolve(`任务${id}的结果`);
    }, delay);
  });
}
// 2. 创建一个请求池,限制并发为 2

const pool = new RequesetPool(2);
// 3. 把你的请求扔进去
for (let i = 0; i < 10; i++) {
  pool.add(() => MockApi(i)).then((result: any) => console.log(`[${i}] 收到的结果:${result}`));
}

发生了什么?

当你运行上面的代码,你会看到:

  1. [1] 和 [2] 的请求几乎同时开始。
  2. [3][4][5][6] 在乖乖排队。
  3. 当 [1] 或 [2] 中任意一个完成后,队列中的 [3] 马上就会开始。
  4. 整个过程,同时运行的请求数永远不会超过 2 个

控制台输出类似这样:

发生了什么?

当你运行上面的代码,你会看到:

  1. [1] 和 [2] 的请求几乎同时开始。
  2. [3][4][5][6] 在乖乖排队。
  3. 当 [1] 或 [2] 中任意一个完成后,队列中的 [3] 马上就会开始。
  4. 整个过程,同时运行的请求数永远不会超过 2 个

控制台输出类似这样:


网站公告

今日签到

点亮在社区的每一天
去签到