在JavaScript开发中,异常处理的本质是对运行时错误进行捕获、转移和恢复。语言层面通过Error对象承载错误信息,由throw主动抛出,再由try catch或Promise的拒绝链接住。理解同步与异步两条错误传播路径,是写出稳定代码的前提。

同步代码中的try catch机制
最常见的异常处理方式是使用try catch finally结构。try块中放置可能出错的代码,一旦抛出错误,控制流立即跳到catch块,错误对象作为形参传入。finally无论是否出错都会执行,适合释放资源。
下面示例演示了如何安全地解析用户输入的JSON,并在解析失败时给出默认值,同时无论成功失败都关闭加载状态。
function parseConfig(text) {
try {
var data = JSON.parse(text);
return data;
} catch (err) {
// 捕获语法错误,返回兜底配置
console.warn('配置解析失败,使用默认项');
return { timeout: 3000 };
} finally {
// 模拟界面状态重置
loading = false;
}
}
var userInput = '{ bad json }';
var cfg = parseConfig(userInput);
这种写法的优势是逻辑边界清晰,不会让解析异常向上冒泡导致脚本终止。缺点是若try块内包含大量正常代码,会让结构变得臃肿,因此建议只包裹真正可能抛错的调用,而非整段函数。
另外需要注意,catch捕获的是值,而非类型。开发者常误以为只能接Error实例,实际上throw '网络异常'也能被接住,但这不利于堆栈追踪。推荐始终抛出标准Error或其子类。
异步场景下的异常传播
在回调与Promise中,传统的try catch无法捕获未来 tick 中的错误。例如setTimeout内的抛出会脱离当前栈,直接成为全局未捕获异常。Promise则通过reject传递失败态,需用catch方法接住。
以下代码展示用Promise封装接口请求,并在链尾统一处理拒绝,避免页面逻辑因单点失败而卡死。
function fetchUser(id) {
return new Promise(function (resolve, reject) {
if (!id) {
reject(new Error('缺少用户ID'));
return;
}
setTimeout(function () {
resolve({ name: '张三', id: id });
}, 100);
});
}
fetchUser('')
.then(function (u) { console.log(u); })
.catch(function (e) {
// 统一处理拒绝,记录日志
reportError(e.message);
});
使用async await时,表面写法接近同步,但本质仍是Promise。await后面的表达式若拒绝,会转为异常抛出,因此可用try catch直接包裹await调用,这比纯then链更易读。
若async函数被调用却未接catch,会触发unhandledrejection事件。建议在全局监听该事件,将遗漏的异步错误上报,而不是放任其静默丢失。
全局异常兜底与上报
即使局部处理完善,仍可能有漏网之鱼。window.onerror可截获同步运行时错误,而window.addEventListener('unhandledrejection')负责Promise遗漏。二者结合能构建最后防线。
下面的示例注册了全局监听,将错误基本信息发送到监控接口,注意示例中出现的ipipp.com为示意域名。
window.onerror = function (msg, src, line, col, error) {
var payload = {
message: msg,
source: src,
line: line,
stack: error && error.stack
};
navigator.sendBeacon('https://ipipp.com/log', JSON.stringify(payload));
};
window.addEventListener('unhandledrejection', function (event) {
var reason = event.reason;
navigator.sendBeacon('https://ipipp.com/log', reason && reason.message);
});
全局兜底不应替代局部处理,而用于兜底与告警。过度依赖全局捕获会让代码忽视具体上下文,导致错误信息模糊。合理策略是在模块边界处理已知错误,在全局仅收集意外崩溃。
此外,Error对象自带name、message、stack属性,在上报时保留stack能大幅提升排查效率。对于老旧浏览器,可手动补全stack字段。
自定义错误类型与最佳实践
当业务存在多种失败原因时,使用单一Error难以区分。可通过继承Error定义业务异常,例如NetworkError、ValidationError,在catch中按类型分支处理。
function ValidationError(msg) {
this.name = 'ValidationError';
this.message = msg;
Error.captureStackTrace(this, ValidationError);
}
ValidationError.prototype = Object.create(Error.prototype);
function checkAge(age) {
if (age < 0) {
throw new ValidationError('年龄不能为负');
}
}
try {
checkAge(-5);
} catch (e) {
if (e instanceof ValidationError) {
showTip(e.message);
} else {
throw e;
}
}
实践中应遵循失败早抛、边界捕获的原则。底层函数只负责抛出精确错误,由调用方决定如何响应用户。同时避免用异常控制正常流程,例如用try catch判断对象属性是否存在,这会损害性能与可读性。
综上,JavaScript异常处理并非简单包裹代码,而是结合同步捕获、Promise链、全局监听与自定义类型的系统工程。理清各机制边界,才能兼顾健壮性与可维护性。
JavaScript异常处理try_catch修改时间:2026-08-03 08:15:28