JS 中 try-catch 异常处理机制详解(结合 async/await)
1. 基本 try-catch 语法
1.1 基础用法
try {
// 可能抛出异常的代码
const result = riskyOperation();
console.log('成功:', result);
} catch (error) {
// 异常处理
console.error('捕获到错误:', error.message);
} finally {
// 无论是否发生异常都会执行
console.log('清理工作完成');
}
1.2 Error 对象详解
try {
throw new Error('自定义错误信息');
} catch (error) {
console.log(error.name); // "Error"
console.log(error.message); // "自定义错误信息"
console.log(error.stack); // 堆栈跟踪信息
// 自定义错误类型
if (error instanceof TypeError) {
console.log('类型错误');
} else if (error instanceof RangeError) {
console.log('范围错误');
} else if (error instanceof SyntaxError) {
console.log('语法错误');
}
}
2. 同步代码中的异常处理
2.1 同步异常示例
function divide(a, b) {
if (b === 0) {
throw new Error('除数不能为零');
}
return a / b;
}
function processData() {
try {
const result = divide(10, 0);
console.log('结果:', result);
} catch (error) {
console.log('计算失败:', error.message);
// 可以选择重新抛出
// throw error;
}
}
processData();
3. 异步代码中的异常处理
3.1 Promise 的异常处理
// 方式1:使用 catch 方法
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('请求失败:', error))
.finally(() => console.log('请求结束'));
// 方式2:在 then 中处理错误
fetch('https://api.example.com/data')
.then(
response => response.json(),
error => console.error('请求失败:', error)
);
3.2 async/await 中的异常处理
3.2.1 基本用法
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status}`);
}
const data = await response.json();
console.log('数据:', data);
return data;
} catch (error) {
console.error('获取数据失败:', error.message);
// 返回默认值或重新抛出
return { defaultValue: true };
}
}
3.2.2 多个异步操作
async function processMultipleRequests() {
try {
// 并行请求
const [user, posts] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/posts').then(r => r.json())
]);
console.log('用户:', user);
console.log('帖子:', posts);
} catch (error) {
console.error('请求失败:', error);
}
}
4. 高级用法和最佳实践
4.1 封装错误处理函数
// 错误处理装饰器
function withErrorHandling(fn) {
return async function(...args) {
try {
return await fn(...args);
} catch (error) {
console.error(`函数 ${fn.name} 执行失败:`, error);
// 根据错误类型进行不同处理
if (error instanceof NetworkError) {
// 网络错误处理
return retryOperation(fn, args);
} else if (error instanceof ValidationError) {
// 验证错误处理
throw new UserFriendlyError('输入数据有误');
}
throw error; // 重新抛出未知错误
}
};
}
// 使用装饰器
const safeFetchData = withErrorHandling(fetchData);
4.2 自定义错误类
// 自定义错误类型
class AppError extends Error {
constructor(message, code = 'UNKNOWN_ERROR') {
super(message);
this.name = this.constructor.name;
this.code = code;
this.timestamp = new Date().toISOString();
}
}
class NetworkError extends AppError {
constructor(message) {
super(message, 'NETWORK_ERROR');
}
}
class ValidationError extends AppError {
constructor(message, field) {
super(message, 'VALIDATION_ERROR');
this.field = field;
}
}
// 使用自定义错误
async function validateUser(input) {
if (!input.email) {
throw new ValidationError('邮箱不能为空', 'email');
}
try {
await saveToDatabase(input);
} catch (error) {
throw new NetworkError('保存数据失败: ' + error.message);
}
}
4.3 全局错误处理
// 全局 Promise 错误处理
window.addEventListener('unhandledrejection', event => {
console.error('未处理的 Promise 拒绝:', event.reason);
event.preventDefault(); // 防止默认错误处理
});
// 全局错误处理
window.addEventListener('error', event => {
console.error('全局错误:', event.error);
// 可以发送到错误监控服务
sendToErrorMonitoring(event.error);
});
5. 实际应用场景
5.1 API 请求封装
class ApiClient {
constructor(baseURL) {
this.baseURL = baseURL;
}
async request(endpoint, options = {}) {
try {
const response = await fetch(`${this.baseURL}${endpoint}`, {
headers: { 'Content-Type': 'application/json' },
...options
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new ApiError(
errorData.message || `HTTP ${response.status}`,
response.status,
errorData
);
}
return await response.json();
} catch (error) {
if (error instanceof ApiError) {
throw error;
}
// 网络错误
if (error.name === 'TypeError' && error.message.includes('fetch')) {
throw new NetworkError('网络连接失败');
}
throw error;
}
}
}
// 使用示例
const api = new ApiClient('https://api.example.com');
async function getUsers() {
try {
const users = await api.request('/users');
return users;
} catch (error) {
if (error instanceof NetworkError) {
// 显示网络错误提示
showNotification('网络连接失败,请检查网络');
} else if (error instanceof ApiError) {
// 显示API错误信息
showNotification(error.message);
}
return [];
}
}
5.2 表单验证与提交
async function submitForm(formData) {
try {
// 验证数据
validateFormData(formData);
// 提交数据
const response = await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(formData)
});
if (!response.ok) {
throw new Error('提交失败');
}
const result = await response.json();
showSuccessMessage('提交成功');
return result;
} catch (error) {
if (error instanceof ValidationError) {
// 显示字段级错误
highlightErrorField(error.field, error.message);
} else {
// 显示通用错误
showErrorMessage(error.message || '发生未知错误');
}
// 记录错误日志(非敏感信息)
logError(error, { formId: 'user-registration' });
// 重新抛出错误供上层处理
throw error;
} finally {
// 清理工作,如禁用加载状态
setLoadingState(false);
}
}
6. 最佳实践总结
明确错误类型:使用自定义错误类,使错误处理更有针对性
不要静默忽略错误:即使只是记录,也要处理所有可能的错误
适当重新抛出:在 catch 块中处理后,如果无法完全恢复,应重新抛出
使用 finally:清理资源,无论是否发生错误
异步错误传播:注意 async 函数总是返回 Promise,错误可能传播到调用者
错误边界:在 React 等框架中,使用错误边界处理组件树中的错误
用户体验:向用户展示友好的错误信息,而不是技术细节
监控和日志:将重要错误发送到监控系统
// 综合示例
async function robustAsyncOperation() {
let resource = null;
try {
resource = await acquireResource();
const data = await processWithResource(resource);
return await validateAndTransform(data);
} catch (error) {
if (error instanceof ValidationError) {
console.warn('数据验证失败,使用默认值');
return getDefaultData();
}
if (error instanceof TemporaryError) {
console.log('临时错误,重试中...');
return await retryOperation();
}
console.error('严重错误:', error);
throw new CriticalError('操作失败', { cause: error });
} finally {
if (resource) {
await releaseResource(resource);
}
}
}
通过合理使用 try-catch 和 async/await,可以编写出健壮、可维护的异步 JavaScript 代码。关键是根据具体场景选择合适的错误处理策略,既不过度捕获也不遗漏重要错误。