try-catch仅捕获同步错误,因异步任务在调用栈清空后执行,此时try块上下文已销毁;async/await通过将Promise拒绝转译为同步异常,使catch可捕获,本质是语法糖而非try-catch自身能力扩展。

try-catch 本身不支持异步捕获,它只作用于当前执行上下文中的同步错误。所谓“能捕获异步错误”,其实是 async/await 与 try-catch 协同工作的结果,不是 try-catch 自身能力的扩展。
为什么原生 try-catch 抓不住 setTimeout 或 Promise.then 中的错误
核心在于 JavaScript 的执行模型:
- 异步任务(如 setTimeout 回调、Promise 微任务)被推入任务队列,等当前调用栈清空后才执行;
- try-catch 块在同步代码执行完就退出了,其对应的执行上下文和错误监听机制已销毁;
- 当异步回调真正运行并 throw 错误时,已不在任何 try 块的保护范围内,错误直接变成未捕获异常(Uncaught Error 或 Unhandled Rejection)。
async/await 是如何让 try-catch “看起来”能捕获异步错误的
async 函数返回 Promise,await 关键字会暂停函数执行,但不会阻塞线程——它把后续逻辑自动包装进 Promise 链的 .then() 和 .catch() 中。
- 当 await 后的 Promise 被 reject,V8 引擎会在内部将其转换为一个同步抛出的错误(语义上等价于 throw),并立即触发最近的 catch 块;
- 这个过程由语言规范保证(ECMAScript 2017+),底层依赖引擎对 async 函数的字节码编排和 Promise 状态机的联动;
- 本质上,是 await 把异步失败“转译”成了同步可捕获的异常事件,而非 try-catch 主动跨任务队列监听。
Promise 链自身的错误处理才是根本机制
即使不用 try-catch,Promise 也自带错误传播路径:
-
.then(onFulfilled, onRejected)的第二个参数可捕获前一个 Promise 的 rejection; -
.catch(fn)等价于.then(null, fn),会捕获链中任意环节的 rejection; - async/await 只是语法糖,最终仍编译为基于 Promise.prototype.catch 的错误处理结构。
无法绕过的边界:哪些异步错误依然捕获不到
以下情况即使用了 async/await + try-catch 也无法拦截:
- 全局未处理的 Promise rejection(如漏写 .catch 或顶层 await 失败且无 try 包裹);
- 事件处理器中的错误(如
img.onerror = () => { throw … }),因为事件回调是独立执行上下文; - 定时器回调、Web Worker 内部、或跨域脚本抛出的错误,它们脱离当前 JS 执行流,不受当前 try-catch 影响。

















