Promise限制重复请求

前端并发控制之:请求去重与共享模式 (Shared Promise)

在前端并发控制中,除了限制"最大并发数"(如限制上传 10 个文件),还有一种常见的场景是限制"重复请求"。

当多个组件(或逻辑)在极短时间内几乎同时调用同一个接口(例如:获取数据字典、用户信息、Token)时,如果每次都发起真实的 HTTP 请求,会导致带宽浪费和后端压力。

核心解决方案:Promise 共享模式。


核心原理

"查缓存 -> 查正在进行中 -> 发起新请求"

该模式将数据获取过程分为三个优先级步骤:

  1. 命中缓存:如果数据已经拿到了,直接返回数据。
  2. 命中并发(Promise 共享) :如果请求正在进行中(Pending),不发新请求,而是直接返回正在进行的那个 Promise。
  3. 发起请求:如果既没缓存也没在请求,才真正发起网络请求,并保存 Promise 引用。

代码范式

javascript 复制代码
// 状态变量(通常定义在 Store 或 Service 中)
let dataCache = null;      // 1. 数据缓存
let loadingPromise = null; // 2. 保存正在进行的 Promise 引用

async function getData() {
  // ①【查缓存】:已有数据,直接返回
  if (dataCache) return dataCache;

  // ②【查并发】:请求正在进行中,返回同一个 Promise(搭便车)
  // 关键点:这一步实现了"多个调用,一个请求"
  if (loadingPromise) return loadingPromise;

  // ③【发起新请求】:
  // 将请求赋值给 loadingPromise,供后续的调用方复用
  loadingPromise = fetch('/api/data')
    .then(res => res.json())
    .then(res => {
      dataCache = res; // 写入缓存
      return res;
    })
    .catch(err => {
      // 慎重:如果失败,通常需要重置 cache 和 promise,允许重试
      dataCache = null;
      throw err;
    })
    .finally(() => {
      // ④【清理状态】:无论成功失败,请求结束了,不再处于 loading 状态
      // 必须置空,否则下一次请求无法发起
      loadingPromise = null;
    });

  return loadingPromise;
}

场景对比

维度 并发数限制 (p-limit) 请求去重 (Shared Promise)
针对对象 不同的任务 (如上传100张不同的图) 相同的任务 (如10个组件都要获取UserInfo)
目的 保护服务器/浏览器不被撑爆 避免做无用功,节省流量
策略 排队执行,慢慢来 合并执行,大家共用一份结果
返回结果 每个任务返回各自的结果 所有调用方拿到完全相同的结果

优缺点分析

优点

  1. 极致性能 :在组件化开发中,避免了 created/mounted 钩子中重复发请求的问题。
  2. 数据一致性:所有订阅者拿到的都是同一时刻的数据。
  3. 代码简洁 :对调用方透明,调用方只需要 await getData(),不需要关心是否是并发。

注意事项

  1. finally 清理 :务必在 finally 中将 loadingPromise 置为 null。否则一旦请求结束(无论成功失败),后续的调用依然会拿到旧的 promise,导致状态死锁。
  2. 错误处理:如果请求失败,需要确保缓存不被写入错误数据,且允许下一次重试。

相关推荐
程序员老赵2 小时前
Docker 部署青龙面板:轻松搭建定时任务管理平台
前端·docker·开源
Swot2 小时前
Asciidoctor 公式数字下标错位问题:原因分析与解决方案
前端
Swot2 小时前
leo-editor 中将 Markdown 内容转成 asciidoc 格式
前端
狒狒AI2 小时前
Remotion / GSAP+Three / HyperFrames / Skia 四栈实测:代码渲染知识视频的选栈方法(附开源技能)
前端·人工智能·aigc
修炼的dance2 小时前
浏览器里导出H.265,8.5秒和55.5秒差在硬件
前端
补码缩写2 小时前
WebCodecs选编码器,探测之后还要验解码和播放
前端
大文说跨境2 小时前
亚马逊多账号管理浏览器怎么选?云登/紫鸟/AdsPower 对比
java·前端·自动化
hunter_0762 小时前
iOS 首页复杂嵌套滚动技术方案:头部固定 + 网格滚动 + Tab 吸顶 + 每 Tab 独立下拉刷新
前端
CoderYanger2 小时前
Java EE 进阶:4.Spring Web MVC 入门(第一弹)
java·开发语言·前端·spring·面试·java-ee·mvc
用户2788607285672 小时前
fallback 会盖掉平台提示词:system、空数组和 systemAppend 的差别
前端·后端