try catch 仅捕获同步异常,如 throw、ReferenceError、TypeError、SyntaxError;对异步错误、事件处理器错误、Promise 错误无效;需用 .catch() 或 async/await 配合;应记录 e.name 和 e.stack;finally 中抛错会覆盖原错误。
它只捕获**同步执行过程中抛出的异常**,比如 throw new Error()、引用未定义变量(ReferenceError)、调用非函数值(TypeError)、JSON 解析失败(SyntaxError)等。但对以下情况完全无效:
setTimeout、Promise 回调、fetch 失败)onclick 里 throw)SyntaxError)——这类错在解析阶段就报了,根本进不了 try 块写成这样是徒劳的:
try {
fetch('/api/data').then(res => res.json()).then(data => console.log(data));
} catch (e) {
console.error('这根本不会执行');
}
因为 fetch 立即返回一个 Promise,try 块结束时它还没 resolve 或 reject。正确做法是:
.catch() 链式处理:fetch(...).then(...).catch(e => {...})
async/await 配合 try/catch(注意函数必须是 async)async function loadData() {
try {
const res = await fetch('/api/data');
const data = await res.json();
console.log(data);
} catch (e) {
console.error('网络或解析出错了', e);
}
}
很多人只写 catch (e) { console.error(e) },但 e 是个对象,关键信息藏在属性里:
e.message:人类可读的错误描述(如 "Cannot read property 'x' of undefined")e.name:错误类型名("TypeError"、"SyntaxError"),可用于条件判断e.stack:调用栈,定位问题位置最直接的依据e.cause(链式错误原因)生产环境建议至少记录 e.name 和 e.stack,光打 e.toString() 会丢掉上下文。
finally 块总会执行,适合清理资源(如关闭加载状态、清除定时器),但它有两条铁律:
finally 里 throw 或返回一个 rejected Promise,它会覆盖前面 try 或 catch 中的返回值或错误finally 里做可能失败的操作(比如又发一次 fetch),否则可能把原错误“吞掉”常见误用:
try {
riskyOperation();
} catch (e) {
console.error(e);
throw e; // 原意是重抛
} finally {
await cleanup(); // 如果 cleanup() 抛错,上面的 throw 就被覆盖了
}
真正安全的清理,应该确保自身不抛错,或单独捕获其内部异常。