unhandledrejection 是捕获漏掉的 promise 错误最有效、侵入性最小的全局兜底机制,它由浏览器自动触发,覆盖所有未被 .catch() 或 try/catch 处理的拒绝,兼容主流浏览器,标准化提取 reason 并补充上下文后通过 sendbeacon 可靠上报。

直接监听 unhandledrejection 是捕获漏掉的 Promise 错误最有效、侵入性最小的方式。它不依赖业务代码加 .catch(),而是靠浏览器自动把所有“没人管”的拒绝推送到这个事件,天然适合作为全局兜底。
为什么 unhandledrejection 是可靠入口
Promise 链中任意环节(throw、Promise.reject()、async 函数内抛错)只要没被当前链的 .catch() 或 try/catch 拦住,错误就会向后冒泡,直到链尾。若整条链都没处理,浏览器就在微任务队列清空后触发 unhandledrejection——这是唯一稳定、无需改业务逻辑的捕获点。
-
window.onerror完全收不到这类错误,因为它们不在同步执行栈里 - Chrome、Firefox、Edge、Safari 11+ 均已稳定支持,无需 polyfill
- 即使用了
async/await,内部异常也会等价转为 Promise 拒绝,同样命中该事件
标准化 reason 字段,避免上报乱码
事件对象的 reason 可能是 Error 实例、字符串、数字、null,甚至 undefined。直接 JSON.stringify 上报会导致字段缺失或格式混乱。
- 优先提取
reason.message和reason.stack(仅当reason instanceof Error时) - 否则降级为
String(reason),确保日志有内容可查 - 对空值、循环引用、函数等不可序列化字段做安全过滤,防止上报失败
补充关键上下文,提升定位效率
单靠错误内容很难复现问题。在上报前主动附加运行时信息:
- 当前页面 URL(
location.href)、路由路径(location.pathname) - 用户设备标识(
navigator.userAgent)、屏幕尺寸、网络类型(navigator.connection?.effectiveType) - 页面可见状态(
document.visibilityState,判断是否在后台发生) - 统一标记
type: "unhandledrejection",便于服务端分类和告警路由
轻量可靠上报,不拖慢页面也不丢日志
上报本身不能成为故障源:
- 用
navigator.sendBeacon()发送,保证页面卸载(如用户关闭标签页)时仍能发出请求 - 本地节流:同一错误 5 分钟内重复只上报一次,防刷屏
- 离线缓存:网络不可用时暂存至
localStorage,下次页面加载时补发 - 与
window.onerror共用同一个 request ID(如 UUID),让服务端能把同步错误、异步拒绝、资源加载失败关联到同一次用户会话










