Service Worker嵌套Fetch请求逻辑有误,求排查问题原因
嘿,作为Service Worker新手,你尝试用try/catch处理404的思路是可以理解的,但这里的核心问题是异步操作的错误无法被外层同步的try/catch捕获,具体拆解如下:
错误根源:异步代码的错误捕获逻辑不对
Service Worker中的fetch是异步操作,返回的是Promise。你外层的try/catch是同步执行的——当event.respondWith()被调用后,这个同步的try块就已经执行完毕了,而后续fetch的then里抛出的错误,根本触发不到外层的catch分支。
举个直观的例子:同步的try/catch管不到异步函数里的错误,就像这样:
try { setTimeout(() => { throw new Error('异步错误') }, 1000); } catch (err) { // 这里永远不会被触发 }
你的代码就是类似的情况,外层try/catch完全没机会捕获到fetch异步链里抛出的错误。
另外还有个小细节:你写的throw (Error);是抛出了Error构造函数本身,而不是一个错误实例,正确的写法应该是throw new Error('404资源未找到');,不过这不是导致代码失效的主要原因。
修正思路:在Promise链内处理404
既然异步错误要在Promise链里处理,你完全可以在第一个fetch的then里检测到404后,直接发起第二个fetch请求,而不需要用外层的try/catch。比如修改成这样:
self.addEventListener('fetch', function(event) { event.respondWith( // 第一个请求 fetch(event.request) .then(function(response) { if (response.status === 404) { // 404时发起第二个请求 return fetch('/imgs/dr-evil.gif'); } return response; }) .then(function(response) { // 检查第二个请求的状态 if (response.status === 404) { return new Response('couldn\'t fetch twice'); } return response; }) .catch(function() { // 捕获所有请求失败的情况 return new Response("Uh oh, that totally failed!"); }) ); });
这样整个逻辑都在Promise链里流转,无论是第一个请求的404,还是第二个请求的失败,都能被正确处理。
为什么你的原代码不工作?
再回头看你的代码:外层的try/catch在同步阶段就执行完了,当第一个fetch返回404并抛出错误时,这个错误会被fetch链里的.catch(function() { ... })捕获,直接返回了"Uh oh, that totally failed!"的响应,外层的catch分支根本不会被触发,所以你期望的“404时请求另一资源”的逻辑完全没机会执行。
内容的提问来源于stack exchange,提问作者Bentaiba Miled Basma

