捕获 service worker 中的异步错误,核心做法是在所有未处理的 promise 链末端挂载 catch 处理程序,并在全局层面监听 unhandledrejection 事件。由于 service worker 运行在独立的全局作用域中,且没有 dom 环境,传统的 window.onerror 无法生效,必须依赖 self.addeventlistener('unhandledrejection', ...) 来拦截那些未被显式捕获的异步异常。

Service Worker 中哪些异步操作容易抛出未捕获错误
在 Service Worker 的生命周期事件中,如 fetch、install 和 activate,如果直接返回一个 Promise 且该 Promise 内部发生错误而未进行 try-catch 包裹,就会成为未处理的拒绝(Unhandled Rejection)。常见的场景包括网络请求失败、解析 JSON 数据出错或访问不存在的缓存键。

// sw.js
self.addEventListener('fetch', event => {
// 这是一个典型的异步操作
const responsePromise = fetch(event.request)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response;
});
// 如果没有 .catch(),上面的 throw 会导致 unhandledrejection
event.respondWith(responsePromise);
});
// 全局监听未处理的 Promise 拒绝
self.addEventListener('unhandledrejection', event => {
console.error('Unhandled rejection:', event.reason);
// 阻止默认行为,防止控制台报错刷屏
event.preventDefault();
});
上述代码展示了如何在 fetch 事件中通过 event.respondWith 传递 Promise。如果 fetch 成功但后续逻辑抛出异常,而该 Promise 链末尾没有 catch,浏览器就会触发 unhandledrejection 事件。开发者可以在这个监听器中记录日志或上报错误,从而实现对异步错误的捕获。
如何区分同步错误与异步错误的捕获机制
Service Worker 中的同步错误可以通过标准的 try...catch 块捕获,而异步错误则必须依靠 unhandledrejection 事件。两者在处理方式上有显著差异:同步错误会立即中断当前执行流,而异步错误会在微任务队列中传播。理解这一区别有助于正确选择错误处理策略。

// sw.js
self.addEventListener('install', event => {
event.waitUntil(
caches.open('v1').then(cache => {
// 同步错误会被 try-catch 捕获
try {
const data = JSON.parse('{ invalid json }');
} catch (e) {
console.error('Sync error in install:', e);
}
// 异步错误若未 catch,将触发 unhandledrejection
return fetch('/assets/data.json')
.then(res => res.json())
.then(data => cache.put('/data', data));
// 注意:这里缺少 .catch()
})
);
});
self.addEventListener('unhandledrejection', event => {
console.error('Async error caught globally:', event.reason);
event.preventDefault();
});
在 install 事件中,caches.open 返回的 Promise 链中,如果 fetch 或 res.json() 失败,且没有对应的 catch,错误就会冒泡到全局的 unhandledrejection 监听器。相比之下,try...catch 只能捕获同步抛出的异常,无法捕获异步操作中的错误。因此,对于任何涉及 I/O 或网络请求的代码,都应确保 Promise 链完整闭合,或通过全局监听器兜底。
当 unhandledrejection 被触发时,如何获取具体的错误上下文
当 unhandledrejection 事件触发时,event.reason 属性包含了导致拒绝的值,通常是 Error 对象。为了有效调试,需要提取错误消息、堆栈信息以及关联的请求 URL。此外,可以通过 event.promise 获取原始的 Promise 实例,进一步分析其状态。
// sw.js
self.addEventListener('unhandledrejection', async event => {
const reason = event.reason;
// 检查 reason 是否为 Error 对象
if (reason instanceof Error) {
console.error(`Error: ${reason.message}`);
console.error(`Stack: ${reason.stack}`);
// 尝试从堆栈中提取请求 URL
const stackLines = reason.stack.split('\n');
const urlMatch = stackLines.find(line => line.includes('http'));
if (urlMatch) {
console.log(`Associated URL: ${urlMatch}`);
}
} else {
console.error('Non-error rejection:', reason);
}
event.preventDefault(); // 防止浏览器默认处理
});
在实际应用中,建议将错误信息发送到远程日志服务,以便集中监控。同时,避免在生产环境中频繁触发 unhandledrejection,因为这可能掩盖真正的编程错误。通过合理设置 event.preventDefault(),可以控制浏览器的默认行为,防止控制台输出过多噪音,同时保留错误数据进行后续分析。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











