async函数内异常不会自动冒泡,必须用await配合try-catch捕获;await是关键节点,仅在promise reject时触发同步异常;未await的异常以promise rejection形式存在,需.catch()或顶层await处理。

async 函数里的异常不会自动冒泡到外层 try-catch,必须显式处理;错误捕获的时机取决于 await 表达式执行时是否抛出异常,而不是函数定义或调用时刻。
await 是异常捕获的关键节点
只有在 await 等待 Promise 被 reject 时,才会触发同步形式的异常(即被 try-catch 捕获)。如果 Promise 被 resolve,即使后续链中发生错误,也不会影响当前 await 的结果。
- await Promise.reject(new Error('fail')) → 立即抛出,可被 surrounding try-catch 捕获
- await Promise.resolve().then(() => { throw new Error('later') }) → 不会被当前 await 捕获,错误会变成未处理的 rejected promise
- 不加 await 直接调用 async 函数,返回的是 Promise,异常会以 Promise rejection 形式存在,需用 .catch() 或顶层 await 处理
推荐的统一错误处理策略
避免混合使用 try-catch 和 .catch(),保持风格一致。多数场景下优先采用 try-catch + await 组合,逻辑清晰且便于调试。
- 对每个可能失败的异步操作单独包裹 try-catch,适合错误需要差异化处理(如重试、降级)
- 多个 await 连续调用时,可共用一个 try-catch 块,但要注意:一旦某个 await 抛错,后续语句不再执行
- 若需“继续执行”,可用 Promise.allSettled() 替代 Promise.all(),它不会因单个失败而中断,返回每个 Promise 的状态对象
顶层 async 函数的异常不能被普通 try-catch 捕获
顶级 async 函数(如模块顶层、IIFE 中的 async () => {...}())抛出的未捕获异常,会表现为 unhandledrejection 事件。浏览器中可通过 window.addEventListener('unhandledrejection') 监听;Node.js 中监听 process.on('unhandledRejection')。
- 不要依赖全局监听来“兜底”业务逻辑错误,它只是最后防线,用于日志记录和监控
- 模块导出的 async 函数,调用方有责任处理其返回的 Promise,例如:api.getUser().catch(handleError)
- Express/Koa 等框架中,可借助中间件统一捕获 next() 传入的 Promise rejection,避免手动每个路由写 try-catch
避免常见陷阱
看似捕获了,实则漏掉错误的情况很常见,核心在于分清“Promise rejection”和“同步 throw”的区别。
- 忘记 await:const p = fetch('/api'); p.catch(...) → 正确;但 let res = fetch('/api'); → res 是 Promise,没 await 就没触发 rejection 捕获
- 在 .then/.catch 链中 throw 错误,但没返回新 Promise 或没被上层 await,会导致隐式 unhandledrejection
- async 函数内部有 setTimeout(() => { throw ... }) 这类纯同步异步代码,不会被任何 await 或 try-catch 捕获,需自行用 try-catch 包裹或改用 Promise 封装
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











