同样是处理并发请求,为什么别人的页面丝滑不卡顿?

在前端开发中,我们经常会遇到一个页面需要发起多个异步请求的场景 ------ 比如列表数据加载、多图片资源请求、批量接口调用等。如果放任这些请求同时发起,它们会相互竞争网络带宽,可能导致部分请求超时、页面加载卡顿,甚至影响用户体验。今天就来聊聊如何通过并发控制,让多个异步请求有序执行,避免带宽被单一请求霸占。

为什么需要控制并发?

想象一个场景:页面同时发起 6 个 AJAX 请求,其中一个请求需要 10 秒才能完成,其余请求分别需要 1-8 秒。如果不做任何控制,浏览器会同时发送这些请求,带宽会被长时间的请求占用,短请求也得排队,最终导致页面加载慢、用户体验差。

并发控制的核心思路是:限制同时运行的请求数量,将超出限制的请求放入队列等待,当有请求完成时,再从队列中取出下一个请求执行。

实现一个通用的并发控制器

接下来我们实现一个通用的并发控制类 Limit,支持自定义并发数,适配任意异步任务(比如 AJAX 请求、Promise 操作等)。

核心代码实现

javascript 复制代码
// 模拟AJAX请求(实际场景可替换为真实接口调用)
function ajax(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // 模拟请求超时/失败(时间>5000视为失败)
      if (time > 5000) {
        reject(new Error(`请求超时:${time}ms`));
      } else {
        resolve(`请求成功:${time}ms`);
      }
    }, time);
  });
}

// 并发控制类
class Limit {
  /**
   * 构造函数
   * @param {number} parallCount - 最大并发数,默认2
   */
  constructor(parallCount = 2) {
    this.tasks = []; // 待执行的任务队列
    this.runningCount = 0; // 正在运行的任务数
    this.parallCount = parallCount; // 最大并发数限制
  }

  /**
   * 添加异步任务到队列
   * @param {Function} task - 返回Promise的异步任务函数
   * @returns {Promise} - 任务执行结果的Promise
   */
  add(task) {
    return new Promise((resolve, reject) => {
      // 将任务和对应的resolve/reject存入队列
      this.tasks.push({
        task,
        resolve,
        reject
      });
      // 尝试执行任务(核心:添加任务后立即触发执行逻辑)
      this._run();
    });
  }

  /**
   * 内部方法:执行队列中的任务
   */
  _run() {
    // 若当前运行任务数未达上限,且队列中有任务,则执行
    if (this.runningCount < this.parallCount && this.tasks.length) {
      // 取出队列第一个任务
      const { task, resolve, reject } = this.tasks.shift();
      this.runningCount++; // 运行任务数+1

      // 执行异步任务
      task()
        .then(result => {
          resolve(result); // 任务成功,传递结果
        })
        .catch(error => {
          reject(error); // 任务失败,传递错误
        })
        .finally(() => {
          // 任务完成(无论成功/失败),运行任务数-1,并继续执行下一个任务
          this.runningCount--;
          this._run();
        });
    }
  }
}

使用示例

我们添加 6 个不同耗时的请求,限制最大并发数为 2,看看执行效果:

javascript 复制代码
// 初始化并发控制器,限制同时运行2个请求
const limit = new Limit(2);

/**
 * 封装添加任务的函数
 * @param {number} time - 请求耗时
 * @param {number} name - 任务名称(用于日志标识)
 */
function addTask(time, name) {
  limit
    .add(() => ajax(time)) // 传入返回Promise的任务函数
    .then(result => {
      console.log(`任务${name}完成:`, result);
    })
    .catch(error => {
      console.log(`任务${name}失败:`, error.message);
    });
}

// 添加6个测试任务
addTask(10000, 1); // 耗时10s(失败)
addTask(4000, 2);  // 耗时4s(成功)
addTask(8000, 3);  // 耗时8s(失败)
addTask(1000, 4);  // 耗时1s(成功)
addTask(5000, 5);  // 耗时5s(成功)
addTask(2000, 6);  // 耗时2s(成功)

执行结果分析

由于并发数限制为 2,任务执行顺序如下:

  1. 初始执行任务 1(10s)和任务 2(4s);

  2. 4s 后任务 2 完成,立即执行任务 3(8s);

  3. 1s 后(总耗时 5s)任务 1 还在执行,任务 3 执行中,无空闲并发位;

  4. 5s 后任务 1 执行到 5s 时仍未完成,但 ajax 函数判定 &gt; 5000ms 触发 reject,任务 1 失败,立即执行任务 4(1s);

  5. 任务 4 完成后执行任务 5(5s);

  6. 任务 3 执行 8s 后失败,执行任务 6(2s);

  7. 最终所有任务按 "并发数 2" 的规则有序执行,避免了带宽竞争。

核心逻辑解析

  1. 队列管理tasks 数组存放待执行的任务,每个任务包含异步函数、resolve 和 reject;

  2. 并发数控制runningCount 实时记录正在运行的任务数,parallCount 为最大并发数限制;

  3. 自动执行 :每次调用 add 添加任务后,立即触发 \_run 方法,尝试从队列中取出任务执行;

  4. 任务收尾 :无论任务成功或失败,最终都会通过 finally 减少运行数,并再次调用 \_run,实现队列任务的自动衔接。

扩展与优化

这个基础版本的并发控制器可以根据实际需求扩展:

  • 取消任务:添加取消队列中指定任务的方法;

  • 优先级队列:支持按优先级执行任务,而非先进先出;

  • 进度回调:添加全局进度回调,实时返回任务执行进度;

  • 错误重试:对失败的任务支持自动重试,可配置重试次数;

  • 批量添加:支持一次性添加多个任务,简化调用逻辑。

总结

前端并发请求控制是解决多请求带宽竞争的关键手段,通过限制并发数、队列管理的方式,让异步请求有序执行,既能保证网络资源的合理利用,也能提升页面加载的稳定性和用户体验。

本文实现的 Limit 类是一个通用的并发控制方案,不仅适用于 AJAX 请求,还能适配任何返回 Promise 的异步任务(比如文件上传、定时器操作等)。你可以根据自己的业务场景,基于这个基础版本扩展更多实用功能。

相关推荐
一步一个脚印一个坑1 小时前
页面性能监控中”资源加载”指标的深度解析:为什么静态资源加载时间和页面资源加载时间对不上?
前端
是你的小橘呀1 小时前
模型总说瞎话?RAG 技术帮你用私域数据精准 “校准” 大模型
前端
云水一下1 小时前
HTML5 从入门到精通:不止于标签——HTML5 高级特性,小交互无需 JavaScript
前端·html5
来自上海的这位朋友1 小时前
Spring Boot + MySQL 搭一个多人游戏后端:登录、房间、匹配、对局和成长系统
前端·后端·three.js
来自上海的这位朋友1 小时前
浏览器里的实时对局同步:WildHunt 的 WebSocket、输入序号与服务端快照
前端·javascript·后端
徐安安ye1 小时前
FlashAttention前端优化:Token合并、MergeNet与冗余计算消除
前端
吃炸鸡的前端1 小时前
react-hook-from从入门到精通
前端·javascript·react.js
来恩10032 小时前
jQuery对Ajax的支持
前端·ajax·jquery
KaMeidebaby2 小时前
卡梅德生物技术快报|抗体的制备与纯化:分子实验实操:番茄 sHSP 重组表达与抗体的制备与纯化工艺
前端·数据库·人工智能·其他·算法·百度·新浪微博