知识篇 -- JS错误处理与健壮性
Ray Shine
2024/1/26 JavaScript进阶知识错误处理
本文为博主原创文章,遵循
CC 4.0 BY-SA
版权协议,转载请附上原文出处链接和本声明。
如有侵权,请联系
本博主
删除。
在JavaScript编程中,错误是不可避免的。无论是语法错误、运行时错误还是逻辑错误,都可能导致程序中断或行为异常。有效的错误处理机制是构建健壮、可靠Web应用的关键。本文将深入探讨JavaScript中错误处理的各种方法,从基本的 try...catch 语句到自定义错误,以及异步操作中的错误处理。
# 一、try...catch 语句:捕获同步错误 同步
try...catch 是JavaScript中最基本的错误处理结构,用于捕获和处理同步代码中可能发生的错误。
try块:包含可能抛出错误的代码。catch块:当try块中的代码抛出错误时,catch块会被执行,并接收一个Error对象作为参数。finally块 (可选):无论try块中的代码是否抛出错误,也无论catch块是否被执行,finally块中的代码总会被执行。通常用于资源清理。
语法:
try {
// 可能会抛出错误的代码
} catch (error) {
// 错误发生时执行的代码
// error 参数包含了错误信息
} finally {
// 无论是否发生错误,都会执行的代码
}
示例:
function divide(a, b) {
try {
if (b === 0) {
throw new Error("除数不能为0"); // 手动抛出错误
}
return a / b;
} catch (error) {
console.error("捕获到错误:", error.message); // 捕获并处理错误
return NaN; // 返回一个特殊值表示失败
} finally {
console.log("除法操作尝试完成。");
}
}
console.log(divide(10, 2)); // 输出:5, 除法操作尝试完成。
console.log(divide(10, 0)); // 输出:捕获到错误: 除数不能为0, 除法操作尝试完成。, NaN
# 二、Error 对象:错误的详细信息 错误类型
当JavaScript运行时发生错误时,会创建一个 Error 对象(或其子类的实例),其中包含了错误的详细信息。
Error对象的属性:name:错误的类型(如 "ReferenceError", "TypeError", "RangeError")。message:错误的详细描述信息。stack:错误的堆栈跟踪信息,有助于定位错误发生的位置。
常见的
Error子类:ReferenceError:引用了未声明的变量。TypeError:操作使用了错误的数据类型或值。RangeError:数字超出有效范围。SyntaxError:代码中存在语法错误(通常在解析阶段就会报错,无法被try...catch捕获)。URIError:URI编码或解码函数使用不当。EvalError:eval()函数使用不当(ES6后已不常用)。
示例:
try {
const x = y + 1; // y未定义
} catch (error) {
console.log("错误名称:", error.name); // ReferenceError
console.log("错误信息:", error.message); // y is not defined
console.log("错误堆栈:", error.stack); // 堆栈跟踪信息
}
# 三、自定义错误:创建更具语义的错误类型 扩展
在复杂的应用中,内置的 Error 类型可能不足以表达所有业务逻辑错误。我们可以通过继承 Error 类来创建自定义错误类型。
示例:
class CustomValidationError extends Error {
constructor(message, field) {
super(message); // 调用父类Error的构造函数
this.name = "CustomValidationError"; // 设置自定义错误名称
this.field = field; // 添加自定义属性
}
}
function validateInput(input) {
if (!input || input.length < 5) {
throw new CustomValidationError("输入长度不足", "username");
}
return true;
}
try {
validateInput("abc");
} catch (error) {
if (error instanceof CustomValidationError) {
console.error(`自定义验证错误: ${error.message} (字段: ${error.field})`);
} else {
console.error("未知错误:", error);
}
}
# 四、异步错误处理:Promise与Async/Await 异步
同步代码中的错误可以通过 try...catch 轻松捕获,但异步代码的错误处理则需要特殊考虑。
# 1. Promise中的错误处理 Promise
.catch()方法:Promise链中的任何一个reject都会被最近的.catch()捕获。Promise.prototype.finally()(ES2018新增):无论Promise成功或失败,都会执行的回调函数,常用于清理工作。
示例:
function asyncOperation() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve("异步操作成功!");
} else {
reject(new Error("异步操作失败!"));
}
}, 1000);
});
}
asyncOperation()
.then(result => console.log(result))
.catch(error => console.error("Promise捕获到错误:", error.message))
.finally(() => console.log("异步操作完成。"));
注意:如果没有 .catch() 捕获 rejected 的Promise,错误会冒泡到全局,导致未捕获的Promise拒绝错误。
# 2. Async/Await中的错误处理 Async/Await
async/await 使得异步错误处理变得和同步错误处理一样简单,可以直接使用 try...catch。
示例:
async function performAsyncTasks() {
try {
console.log("开始执行异步任务...");
const result = await asyncOperation(); // await会等待Promise解决或拒绝
console.log(result);
} catch (error) {
console.error("Async/Await捕获到错误:", error.message);
} finally {
console.log("异步任务执行完毕。");
}
}
performAsyncTasks();
# 五、全局错误处理 全局
window.onerror:用于捕获未被try...catch捕获的全局运行时错误。window.onerror = function(message, source, lineno, colno, error) { console.error("全局错误:", message, source, lineno, colno, error); // 返回 true 可以阻止浏览器默认的错误处理(例如在控制台打印错误) return true; }; // 故意制造一个错误 // nonExistentFunction();window.addEventListener('unhandledrejection', ...):用于捕获未被处理的Promise拒绝错误。window.addEventListener('unhandledrejection', function(event) { console.error("未处理的Promise拒绝:", event.promise, event.reason); // 阻止默认行为(例如在控制台打印警告) event.preventDefault(); }); // 故意制造一个未捕获的Promise拒绝 // Promise.reject("Something went wrong!");
有效的错误处理是构建高质量JavaScript应用不可或缺的一部分。通过合理运用 try...catch、Promise链、Async/Await以及全局错误监听,可以确保程序在面对异常情况时依然能够优雅地运行,并提供有用的反馈。