You need to enable JavaScript to run this app. Service Worker fetch重入:如何避免相同REST请求重复发送
优惠活动
大模型
产品
解决方案
定价
更多

Service Worker fetch重入:如何避免相同REST请求重复发送

Service Worker REST 缓存并发重复请求优化方案

核心问题根因

原有「网络响应即缓存」的实现逻辑中,缓存的写入时机为网络请求拿到响应之后。当短时间内连续发起多个相同请求时,所有请求检测到的已落盘缓存状态都是一致的(都未写入),因此会各自发起网络请求,造成冗余调用。

优化核心思路

  • 新增内存级的进行中请求映射表,在网络请求尚未完成、未写入磁盘缓存的阶段,将对应请求的Promise实例存入映射表,相同的并发请求直接复用该Promise实例,无需重复发起网络调用。

实现细节

  • 仅对需要走缓存策略的幂等REST请求(默认GET方法)生效,非幂等请求直接放行,避免写操作被错误复用
  • 请求结果查找优先级:磁盘已落盘缓存 > 内存中pending的请求映射 > 新发起网络请求
  • 所有pending请求在完成(成功/失败)后,必须从映射表中清除对应条目,避免内存泄漏或异常状态阻塞后续请求

优化后核心实现代码

// 全局维护进行中请求的映射关系:key为请求唯一标识,value为未完成的请求Promise
const pendingRequestMap = new Map();

// 生成请求唯一匹配key:匹配请求方法+完整URL,避免误匹配
function generateRequestKey(request) {
  return `${request.method}-${request.url}`;
}

self.addEventListener('fetch', function (event) {
  // 非GET请求不参与缓存复用逻辑
  if (event.request.method !== 'GET') {
    return;
  }

  event.respondWith(
    caches.open('mysite-dynamic').then((cache) => {
      return cache.match(event.request).then((cacheRes) => {
        // 磁盘缓存命中直接返回
        if (cacheRes) return cacheRes;

        const requestKey = generateRequestKey(event.request);
        // 相同请求正在进行中,直接复用pending的Promise
        if (pendingRequestMap.has(requestKey)) {
          return pendingRequestMap.get(requestKey);
        }

        // 无可用缓存、无进行中请求,才发起新网络请求
        const fetchTask = fetch(event.request).then((networkRes) => {
          // 仅缓存成功的响应
          if (networkRes.status === 200) {
            cache.put(event.request, networkRes.clone());
          }
          // 请求完成后清除pending记录
          pendingRequestMap.delete(requestKey);
          return networkRes;
        }).catch((err) => {
          // 请求失败也要清除pending记录,避免阻塞后续相同请求
          pendingRequestMap.delete(requestKey);
          throw err;
        });

        // 将发起的请求存入映射表
        pendingRequestMap.set(requestKey, fetchTask);
        return fetchTask;
      });
    })
  );
});

注意事项

  • 生成请求匹配key时,需根据实际业务的请求规则调整,比如带动态鉴权头、查询参数的请求,需将影响响应结果的参数纳入key计算,避免不同内容的请求被错误复用
  • Service Worker存在被浏览器回收重启的机制,内存中的pending映射表会在重启后清空,该场景下原有并发连接已随SW终止断开,不会出现重复请求问题,符合预期
  • 如果需要支持带自定义请求头的缓存匹配,可在cache.match时传入匹配参数,兼容跨域、自定义头的场景

内容的提问来源于stack exchange,提问作者vivi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.04 09:54:03