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
相关产品推荐
相关产品推荐

