You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Service Worker仅在对象存在对应文件时Fetch的实现问题求助

嘿,我懂你想实现的逻辑——等localCache里出现指定文件后,再处理对应的Fetch请求对吧?现在的问题是缓存没命中时请求直接失败,还会反复重试,这确实头疼。咱们来一步步解决这个问题:

问题根源

你当前的代码里,当localCache[name]不存在时,loadFile函数没有返回任何有效的Response对象,event.respondWith拿到undefined后,浏览器会判定请求失败,进而触发重试机制。所以核心是要让loadFile返回一个等待缓存就绪后再resolve的Promise,而不是直接返回undefined。

解决方案1:简单轮询等待

这种方式实现起来最直接,通过循环检查缓存状态,直到目标文件出现或超时:

self.addEventListener('fetch', event => { 
  event.respondWith(loadFile(event.request.url)) 
}) 

async function loadFile(url, timeout = 30000) { // 默认30秒超时,可按需调整
  const name = fileName(url);
  const startTime = Date.now();

  // 循环等待缓存出现
  while (!localCache[name]) {
    // 检查是否超时
    if (Date.now() - startTime > timeout) {
      return new Response('文件在缓存中超时未出现', { status: 404 });
    }
    // 每隔100ms检查一次,间隔可自定义
    await new Promise(resolve => setTimeout(resolve, 100));
  }

  // 缓存就绪后返回响应
  return new Response(localCache[name]);
}

优点:代码简单,不需要修改缓存更新的逻辑;
缺点:存在固定间隔的延迟,且会持续占用少量资源做轮询。

解决方案2:高效的发布订阅模式

如果localCache的更新是由你可控的代码触发的(比如有专门的逻辑往里面添加文件),那这种方式更高效——缓存更新时主动通知等待的请求,避免无用轮询:

// 存储每个文件的等待Promise队列
const cacheWaiters = new Map();

// 请在更新localCache的地方调用这个函数,通知等待的请求
function updateLocalCache(name, content) {
  localCache[name] = content;
  // 如果有等待该文件的请求,触发它们的resolve
  if (cacheWaiters.has(name)) {
    const waiters = cacheWaiters.get(name);
    waiters.forEach(resolve => resolve());
    cacheWaiters.delete(name);
  }
}

self.addEventListener('fetch', event => { 
  event.respondWith(loadFile(event.request.url)) 
}) 

async function loadFile(url, timeout = 30000) {
  const name = fileName(url);

  // 缓存已存在,直接返回
  if (localCache[name]) {
    return new Response(localCache[name]);
  }

  // 创建等待缓存更新的Promise
  let resolveWaiter;
  const waitPromise = new Promise(resolve => {
    resolveWaiter = resolve;
  });

  // 将Promise加入对应文件的等待队列
  if (!cacheWaiters.has(name)) {
    cacheWaiters.set(name, []);
  }
  cacheWaiters.get(name).push(resolveWaiter);

  // 设置超时逻辑
  const timeoutPromise = new Promise((_, reject) => {
    setTimeout(() => reject(new Error('等待缓存超时')), timeout);
  });

  try {
    // 等待缓存更新或超时
    await Promise.race([waitPromise, timeoutPromise]);
    return new Response(localCache[name]);
  } catch (err) {
    // 超时后清理当前请求的等待器
    if (cacheWaiters.has(name)) {
      const waiters = cacheWaiters.get(name);
      const index = waiters.indexOf(resolveWaiter);
      if (index !== -1) {
        waiters.splice(index, 1);
        if (waiters.length === 0) {
          cacheWaiters.delete(name);
        }
      }
    }
    // 返回超时响应
    return new Response(err.message, { status: 408 });
  }
}

优点:无延迟,资源占用少;
缺点:需要配合updateLocalCache函数来触发通知,必须在所有更新localCache的地方调用它。

注意事项

  • 无论用哪种方案,都建议加上超时逻辑,避免请求无限期挂起,影响用户体验;
  • 这种等待机制会让请求处于挂起状态,要考虑是否需要在等待期间返回临时响应(比如加载提示),不过这取决于你的业务需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:31:34