在 async 函数中,try-catch 能直接捕获 promise.reject 的原始值,但必须配合 await 使用;await 将 reject 原因作为同步异常抛出,使 catch 拿到未包装的原始值(字符串、error 实例或对象),且链式调用中源头错误透传到底层 catch。

在 async 函数中,try-catch 能直接捕获 Promise.reject 的值,但前提是必须用 await 等待它。这不是“自动包装”或“语法糖”,而是 await 语义的必然结果:当 await 遇到一个被 reject 的 Promise,它会把 reject 的原因当作同步异常抛出,从而进入 catch 块。
await 是关键开关,不是语法装饰
Promise.reject 本身是异步构造行为,不触发 try-catch;只有 await 将其纳入执行流后,错误才可被捕获。
- ❌
try { Promise.reject("oops") }→ 不进 catch,触发 unhandledrejection - ❌
try { someAsyncFn() }(没 await)→ 同样无效,函数只返回 Promise,错误尚未发生 - ✅
try { await Promise.reject("oops") }→ catch 中 e 就是字符串 "oops" - ✅
try { await fetch("/404").then(r => { if (!r.ok) throw new Error(r.status) }) }→ 错误由 throw 触发,等价于 reject
reject 的值原样传递,不做类型转换
catch 拿到的就是 Promise.reject(...) 里传入的原始值,JavaScript 不做隐式封装或转换。
-
Promise.reject("network error")→ catch 中 e === "network error" -
Promise.reject(new TypeError("parse failed"))→ e 是完整 TypeError 实例,含 stack、message、name -
Promise.reject({ code: 500, msg: "timeout" })→ e 可直接访问 e.code 和 e.msg - ⚠️ 注意:throw "xxx" 和 Promise.reject("xxx") 在 async 函数中效果一致,但前者无堆栈,后者若用 Error 实例才有调试信息
链式调用中,源头 reject 值会逐层透出
async 函数调用另一个 async 函数或返回 Promise 的函数时,只要中间没 .catch() 吞掉,最终 await 处拿到的仍是原始 reject 值。
- 函数 A:
async function api() { return fetch("/data").then(r => r.json()) } - 函数 B:
async function load() { try { return await api() } catch (e) { ... } } - 若 fetch 失败,fetch 返回的 Promise 被 reject(TypeError),api() 返回的 Promise 也以该 TypeError 被 reject,load() 的 catch 拿到的就是那个原始 TypeError
- 不会变成 PromiseRejectionEvent 或包装后的对象
如何确保能定位错误来源
想看到准确堆栈,必须让 reject 的值是一个 Error 实例,而非字符串或普通对象。
- ✅
Promise.reject(new Error("DB connection timeout"))→ stack 显示 new Error() 创建位置 - ❌
Promise.reject("DB connection timeout")→ 字符串无 stack,无法追溯到哪行代码 reject 的 - ? 补充技巧:在 catch 中可临时补全,如
throw new Error(e + " at api.js:12"),但原始位置已丢失,仅作日志兜底











