定时器回调中的 await 错误无法被外层 try-catch 捕获,因属新执行上下文;须在回调内用 async/await + try-catch 处理,避免 .then() 抛错穿透,并结合 Promise.allSettled 与 unhandledrejection 全局兜底。

这个问题核心在于:定时器回调本身是宏任务,内部即使写 await,其错误也不会被外层 try-catch 捕获——不是因为 await 失效,而是执行上下文已切换,原 try 块早已退出。
明确错误发生位置
常见误判是以为“写了 await 就等于能 catch 住所有错”。实际上:
-
setTimeout(() => { await api() })中的await属于新执行上下文,外层try对它完全不可见 - 更隐蔽的是微任务穿透:比如
Promise.resolve().then(() => { throw new Error() }),哪怕在await后面,只要抛错发生在新微任务里,就不会进当前catch - 典型陷阱:
await fetch().then(res => res.json())——res.json()报错属于.then()返回的新 Promise,必须单独处理
必须在定时器回调内部加 try-catch
不能依赖外部包裹,必须把防御逻辑下沉到实际执行异步操作的位置:
- 给
setTimeout回调函数自身标记async,并在内部对每个await表达式做try-catch - 示例:✅ 正确
setTimeout(async () => {<br> try {<br> const data = await fetch('/api').then(r => r.json());<br> render(data);<br> } catch (e) {<br> console.error('定时器内请求失败:', e);<br> }<br>}, 1000); - 避免裸写
.then(() => { throw ... }),改用.catch()或await ... .catch(...)显式收口
用 Promise.allSettled 替代链式 await 防连锁中断
当多个独立异步操作并行发起(如定时器里批量拉取数据),单个失败不该阻断其余:
- 别写:
await a(); await b(); await c();—— 一个失败,后面全跳过 - 改用:
const results = await Promise.allSettled([a(), b(), c()]); - 后续遍历
results,对status: 'rejected'的项单独记录或降级处理 - 这样既避免错误穿透,又保留全部执行结果
全局兜底 + 错误分类上报
再严谨的局部处理也可能漏掉边缘路径(如第三方库触发的异步、事件监听器里的 await):
- 在应用初始化时监听
unhandledrejection:
window.addEventListener('unhandledrejection', e => {<br> console.warn('未捕获 Promise 拒绝:', e.reason);<br> // 上报监控系统,附带 URL、时间戳、堆栈<br>}); - 区分错误类型:网络中断、JSON 解析失败、业务校验拒绝,分别走不同恢复策略(重试 / 默认值 / 用户提示)
- 避免只打印日志就结束——至少要确保用户感知(如 toast 提示)或状态可回溯

















