JavaScript的秘密生活:拒绝
发布: (2026年2月20日 GMT+8 16:42)
3 分钟阅读
原文: Dev.to
Source: Dev.to
为什么 async 错误会绕过 try/catch
Timothy 在应用程序的最顶部放置了一个 try/catch 块,确信任何错误都会被捕获:
function loadDashboard() {
try {
// Initiating a background network request
fetch('/api/corrupted-data');
console.log("Dashboard loading...");
} catch (error) {
console.error("Safe Landing:", error.message);
}
}
loadDashboard();
控制台打印了 Dashboard loading…。两秒后进程崩溃,出现:
UnhandledPromiseRejection: Failed to fetch
发生了什么?
fetch()会立即返回一个 pending Promise;它 不会 等待网络请求完成。try块成功结束,调用栈被弹出,catch块随之被丢弃。- 当 Promise 稍后被拒绝时,已经没有原始
try/catch所在的栈帧来处理错误。此时的拒绝会成为 未处理的 Promise 拒绝,现代浏览器和 Node.js 会将其视为致命错误。
将错误处理器附加到 Promise 上
解决办法是 直接在 Promise 上 附加处理器:
function loadDashboard() {
fetch('/api/corrupted-data')
.then(data => console.log("Data loaded!"))
.catch(error => console.error("Safe Landing:", error.message));
console.log("Dashboard loading...");
}
catch方法在 Promise 本身上创建了一个错误边界,当网络请求失败时,拒绝会被路由到该处理器,而不是漂浮到外部。
使用 async/await 配合 try/catch
如果你更喜欢 try/catch 语法,await 会暂停函数执行并保留错误边界:
async function loadDashboard() {
try {
console.log("Dashboard loading..."); // moved before the await
// Execution is suspended here; the try/catch stays in memory.
await fetch('/api/corrupted-data');
console.log("Data loaded successfully!");
} catch (error) {
console.error("Safe Landing:", error.message);
}
}
- 当
await遇到被拒绝的 Promise 时,引擎会 恢复 函数执行,恢复try/catch上下文,并抛出错误,随后被外层的catch捕获。
高级技巧:回调和事件监听器
回调(例如 setTimeout、DOM 事件监听器)会在全新的调用栈中运行。把注册代码包在 try/catch 中 并不能 保护回调体。
// ❌ WRONG: The try/catch runs during setup, not when the event fires.
try {
button.addEventListener('click', () => {
throw new Error("Click failed!"); // crashes the app
});
} catch (e) {
// This block is long gone by the time the click occurs.
}
// ✅ RIGHT: Place the try/catch **inside** the callback.
button.addEventListener('click', () => {
try {
throw new Error("Click failed!"); // caught safely
} catch (e) {
console.error("Safe Landing:", e.message);
}
});