uni.onUnhandledRejection是App端捕获未处理Promise拒绝的唯一可靠方式,必须在App.vue的onLaunch中同步注册,可捕获async/await漏catch或Promise.reject未被处理的场景,但不捕获已catch或Promise.allSettled吞掉的错误。

uni.onUnhandledRejection 是 App 端唯一可靠的未处理 Promise 拒绝监听方式
App(iOS/Android)原生容器不触发 window.addEventListener('unhandledrejection'),也不走标准浏览器事件流。你在页面里写这个监听,或者在 main.js 里注册,基本收不到任何拒绝错误——它只对 H5 有效。
真正起作用的,是 uni-app 提供的桥接 API:uni.onUnhandledRejection。它专为 App 端设计,能捕获:async/await 中漏掉 try/catch 的 throw、Promise.reject() 未被 .catch() 或 await 吞掉的场景。
但注意:它不捕获已 catch 的拒绝,也不捕获被 Promise.allSettled 吞掉的错误——这些本就不算“未处理”。
必须在 App.vue 的 onLaunch 中同步注册,延迟就丢异常
冷启动阶段(App 启动瞬间)发生的 Promise 拒绝,如果监听器还没挂上,就会彻底静默丢失。常见错误包括:
- 把
uni.onUnhandledRejection写在某个页面的onLoad或组件mounted里 - 在
onLaunch中加了异步逻辑(比如先调uni.getSystemInfo再注册) - 用
setTimeout延迟注册,哪怕只延 1ms
正确做法是:在 App.vue 的 onLaunch 第一行就注册,不依赖任何前置条件,不包裹异步操作。
别和 uni.onError 混用同一套上报逻辑,它们语义不同
uni.onError 捕的是同步 JS 异常(如生命周期里 throw new Error()、uni.setStorageSync 参数非法),而 uni.onUnhandledRejection 捕的是异步拒绝。两者堆栈结构、上下文、可恢复性都不同。
容易踩的坑:
- 共用同一个错误日志字段名(比如都塞进
error.message),导致后端无法区分是代码 bug 还是网络超时拒绝 - 在
uni.onUnhandledRejection回调里直接调uni.reLaunch——这会清空路由栈,且无法判断是临时抖动还是逻辑缺陷 - 把拒绝原因(
event.reason)直接当字符串上报,丢失原始Error实例的stack和name
建议结构化上报:type: 'unhandled_rejection'、reason: event.reason?.message || String(event.reason)、stack: event.reason?.stack。
网络请求拒绝不该交由全局兜底,得在封装层拦截重试
很多开发者误以为“请求失败 = 未处理 Promise 拒绝”,于是把所有 uni.request 的 503、超时、证书错误都扔给 uni.onUnhandledRejection 处理。这是错的。
真实情况是:这些错误本该在请求封装函数里就被 catch,转为业务可识别的错误对象,并做有限重试(≤2 次)、loading 锁防重复提交、降级提示等。交给全局拒绝监听,等于放弃控制权,既没法重试,也没法区分状态。
所以重点不在“怎么捕获”,而在“谁该负责捕获”:网络层错误归请求封装,逻辑层拒绝归 uni.onUnhandledRejection,模板渲染错误(如访问 data.xxx[0])这两者都捕不到——那是 Vue 编译期或运行时的限制,得靠类型检查或防御性编程提前规避。


















