当前位置: 首页 > news >正文

优雅的 async/await 错误处理模式指南

优雅的 async/await 错误处理模式指南

告别手动 try-catch,拥抱更优雅的异步错误处理方案

🎯 背景

在现代前端开发中,async/await 已经成为处理异步操作的主流方式。然而,传统的错误处理需要大量的 try-catch 块,代码冗余且不够优雅。本文将介绍几种更优雅的错误处理模式,让你的代码更加简洁和可维护。

😕 传统的 try-catch 问题

常见的冗余代码

// 传统方式 - 每个 async 函数都需要 try-catch
async function fetchUserData(userId) {try {const response = await fetch(`/api/users/${userId}`);const data = await response.json();return data;} catch (error) {console.error('获取用户数据失败:', error);throw error;}
}async function updateUserProfile(userId, profileData) {try {const response = await fetch(`/api/users/${userId}`, {method: 'PUT',body: JSON.stringify(profileData),});return await response.json();} catch (error) {console.error('更新用户资料失败:', error);throw error;}
}// 使用时还需要再次 try-catch
async function handleUserUpdate() {try {const user = await fetchUserData(123);const result = await updateUserProfile(123, { name: '新名字' });console.log('更新成功', result);} catch (error) {// 错误处理showErrorMessage(error.message);}
}

问题分析

  1. 代码重复:每个异步函数都需要类似的错误处理
  2. 嵌套复杂:多层 try-catch 导致代码难以阅读
  3. 维护困难:错误处理逻辑分散,难以统一管理
  4. 忘记处理:容易遗漏某些异步操作的错误处理

🚀 优雅的错误处理方案

1. to() 函数模式 - Go 语言风格

这是一种非常流行的错误处理模式,灵感来自 Go 语言的错误处理方式。

/*** 将 Promise 转换为 [error, data] 元组* @param {Promise} promise - 要处理的 Promise* @returns {Promise<[Error|null, any]>} 返回 [错误, 数据] 的元组*/
function to(promise) {return promise.then((data) => [null, data]).catch((error) => [error, null]);
}// 使用示例
async function fetchUserData(userId) {const [error, data] = await to(fetch(`/api/users/${userId}`).then((res) => res.json()));if (error) {console.error('获取用户数据失败:', error);return null;}return data;
}async function handleUserOperation() {const [userError, user] = await to(fetchUserData(123));if (userError) {showErrorMessage('无法获取用户信息');return;}const [updateError, result] = await to(updateUserProfile(123, { name: '新名字' }));if (updateError) {showErrorMessage('更新失败,请重试');return;}console.log('操作成功', result);
}

2. 增强版 to() 函数

支持类型安全和更多功能的版本:

/*** 增强版错误处理函数* @param promise - 要处理的 Promise* @param errorExt - 可选的错误扩展对象*/
function to<T, U = Error>(promise: Promise<T>,errorExt?: object
): Promise<[U, undefined] | [null, T]> {return promise.then<[null, T]>((data: T) => [null, data]).catch<[U, undefined]>((err: U) => {if (errorExt) {const parsedError = Object.assign(err, errorExt);return [parsedError, undefined];}return [err, undefined];});
}// TypeScript 使用示例
interface User {id: number;name: string;email: string;
}async function fetchUser(id: number): Promise<User | null> {const [error, user] = await to<User>(fetch(`/api/users/${id}`).then((res) => res.json()),{ operation: 'fetchUser', userId: id });if (error) {console.error('获取用户失败:', error);return null;}return user;
}

3. 错误处理装饰器模式

使用装饰器来自动处理错误:

/*** 错误处理装饰器* @param {Function} errorHandler - 错误处理函数*/
function withErrorHandling(errorHandler) {return function (target, propertyKey, descriptor) {const originalMethod = descriptor.value;descriptor.value = async function (...args) {try {return await originalMethod.apply(this, args);} catch (error) {return errorHandler(error, propertyKey, args);}};return descriptor;};
}// 使用示例
class UserService {@withErrorHandling((error, method, args) => {console.error(`${method} 方法执行失败:`, error);return null;})async fetchUser(id) {const response = await fetch(`/api/users/${id}`);return response.json();}@withErrorHandling((error, method, args) => {console.error(`${method} 方法执行失败:`, error);throw new Error('用户操作失败,请重试');})async updateUser(id, data) {const response = await fetch(`/api/users/${id}`, {method: 'PUT',body: JSON.stringify(data),});return response.json();}
}

4. 链式错误处理模式

创建一个支持链式调用的错误处理类:

class AsyncChain {constructor(promise = Promise.resolve()) {this.promise = promise;this.errors = [];}static of(promise) {return new AsyncChain(promise);}then(fn) {this.promise = this.promise.then(async (data) => {if (this.errors.length > 0) return data;try {return await fn(data);} catch (error) {this.errors.push(error);return data;}});return this;}catch(errorHandler) {this.promise = this.promise.then((data) => {if (this.errors.length > 0) {errorHandler(this.errors);this.errors = [];}return data;});return this;}finally(finallyHandler) {this.promise = this.promise.finally(finallyHandler);return this;}execute() {return this.promise;}
}// 使用示例
async function handleComplexOperation() {const result = await AsyncChain.of(Promise.resolve({ userId: 123 })).then(async (data) => {const user = await fetch(`/api/users/${data.userId}`).then((r) =>r.json());return { ...data, user };}).then(async (data) => {const profile = await fetch(`/api/profiles/${data.user.id}`).then((r) =>r.json());return { ...data, profile };}).then(async (data) => {await fetch('/api/analytics', {method: 'POST',body: JSON.stringify({ event: 'user_viewed', userId: data.user.id }),});return data;}).catch((errors) => {console.error('操作中出现错误:', errors);showErrorMessage('操作失败,请重试');}).execute();return result;
}

5. 结果包装器模式

创建一个统一的结果包装器:

class Result {constructor(success, data, error) {this.success = success;this.data = data;this.error = error;}static ok(data) {return new Result(true, data, null);}static error(error) {return new Result(false, null, error);}static async from(promise) {try {const data = await promise;return Result.ok(data);} catch (error) {return Result.error(error);}}isOk() {return this.success;}isError() {return !this.success;}unwrap() {if (this.success) {return this.data;}throw this.error;}unwrapOr(defaultValue) {return this.success ? this.data : defaultValue;}map(fn) {if (this.success) {try {return Result.ok(fn(this.data));} catch (error) {return Result.error(error);}}return this;}mapError(fn) {if (this.isError()) {return Result.error(fn(this.error));}return this;}
}// 使用示例
async function fetchUserProfile(userId) {const userResult = await Result.from(fetch(`/api/users/${userId}`).then((r) => r.json()));if (userResult.isError()) {return userResult.mapError((error) => new Error(`获取用户失败: ${error.message}`));}const profileResult = await Result.from(fetch(`/api/profiles/${userId}`).then((r) => r.json()));return profileResult.map((profile) => ({user: userResult.data,profile,}));
}// 链式操作
async function handleUserData(userId) {const result = await fetchUserProfile(userId);const finalData = result.map((data) => ({...data,displayName: data.user.name || data.profile.nickname,})).map((data) => ({...data,isVip: data.profile.level > 5,})).unwrapOr({ displayName: '未知用户', isVip: false });console.log('处理结果:', finalData);
}

🛠️ 实用工具函数库

创建一个完整的错误处理工具库:

// errorUtils.js
export class ErrorUtils {/*** Go 风格的错误处理*/static to(promise) {return promise.then((data) => [null, data]).catch((error) => [error, null]);}/*** 带重试的异步操作*/static async retry(fn, maxAttempts = 3, delay = 1000) {for (let attempt = 1; attempt <= maxAttempts; attempt++) {const [error, result] = await ErrorUtils.to(fn());if (!error) {return [null, result];}if (attempt === maxAttempts) {return [error, null];}await new Promise((resolve) => setTimeout(resolve, delay * attempt));}}/*** 超时处理*/static withTimeout(promise, timeout = 5000) {const timeoutPromise = new Promise((_, reject) => {setTimeout(() => reject(new Error('操作超时')), timeout);});return Promise.race([promise, timeoutPromise]);}/*** 条件执行*/static async when(condition, asyncFn) {if (typeof condition === 'function') {condition = await condition();}if (condition) {return await asyncFn();}return null;}/*** 安全的 JSON 解析*/static safeJsonParse(str, defaultValue = null) {try {return JSON.parse(str);} catch {return defaultValue;}}/*** 批量处理异步操作*/static async parallel(operations,{ maxConcurrency = 5, failFast = false } = {}) {const results = [];const errors = [];for (let i = 0; i < operations.length; i += maxConcurrency) {const batch = operations.slice(i, i + maxConcurrency);const batchPromises = batch.map(async (op, index) => {const [error, result] = await ErrorUtils.to(op());if (error) {errors.push({ index: i + index, error });if (failFast) {throw error;}return null;}return result;});const batchResults = await Promise.all(batchPromises);results.push(...batchResults);}return { results, errors };}
}// 使用示例
async function complexDataProcessing() {// 1. 带重试的数据获取const [fetchError, userData] = await ErrorUtils.retry(() => fetch('/api/user').then((r) => r.json()),3,1000);if (fetchError) {console.error('获取用户数据失败:', fetchError);return;}// 2. 并行处理多个操作const operations = userData.items.map((item) => () => processItem(item.id));const { results, errors } = await ErrorUtils.parallel(operations, {maxConcurrency: 3,failFast: false,});if (errors.length > 0) {console.warn('部分操作失败:', errors);}console.log('处理完成:', results.filter(Boolean));
}

🎨 在 Vue/React 中的应用

Vue 3 Composition API

// composables/useAsyncData.js
import { ref, unref } from 'vue';
import { ErrorUtils } from './errorUtils';export function useAsyncData(asyncFn, options = {}) {const loading = ref(false);const data = ref(null);const error = ref(null);const {immediate = true,resetOnExecute = true,onError,onSuccess,} = options;const execute = async (...args) => {if (resetOnExecute) {data.value = null;error.value = null;}loading.value = true;const [err, result] = await ErrorUtils.to(asyncFn(...args.map(unref)));loading.value = false;if (err) {error.value = err;onError?.(err);} else {data.value = result;onSuccess?.(result);}return [err, result];};if (immediate) {execute();}return {loading: readonly(loading),data: readonly(data),error: readonly(error),execute,};
}// 在组件中使用
export default {setup() {const { loading, data, error, execute } = useAsyncData((userId) => fetch(`/api/users/${userId}`).then((r) => r.json()),{immediate: false,onError: (err) => console.error('请求失败:', err),onSuccess: (data) => console.log('请求成功:', data),});const loadUser = (id) => execute(id);return {loading,data,error,loadUser,};},
};

React Hook

// hooks/useAsyncOperation.js
import { useState, useCallback } from 'react';
import { ErrorUtils } from '../utils/errorUtils';export function useAsyncOperation(asyncFn, options = {}) {const [loading, setLoading] = useState(false);const [data, setData] = useState(null);const [error, setError] = useState(null);const { onError, onSuccess, resetOnExecute = true } = options;const execute = useCallback(async (...args) => {if (resetOnExecute) {setData(null);setError(null);}setLoading(true);const [err, result] = await ErrorUtils.to(asyncFn(...args));setLoading(false);if (err) {setError(err);onError?.(err);} else {setData(result);onSuccess?.(result);}return [err, result];},[asyncFn, onError, onSuccess, resetOnExecute]);return {loading,data,error,execute,};
}// 在组件中使用
function UserProfile({ userId }) {const { loading, data, error, execute } = useAsyncOperation((id) => fetch(`/api/users/${id}`).then((r) => r.json()),{onError: (err) => toast.error('加载用户信息失败'),onSuccess: (data) => console.log('用户信息加载成功', data),});useEffect(() => {if (userId) {execute(userId);}}, [userId, execute]);if (loading) return <div>Loading...</div>;if (error) return <div>Error: {error.message}</div>;if (!data) return null;return <div>{/* 渲染用户信息 */}</div>;
}

📈 最佳实践

1. 选择合适的模式

  • 简单场景:使用 to() 函数
  • 复杂逻辑:使用 Result 包装器
  • 重复操作:使用装饰器模式
  • 链式操作:使用 AsyncChain

2. 错误分类处理

class AppError extends Error {constructor(message, code, statusCode = 500) {super(message);this.code = code;this.statusCode = statusCode;this.name = 'AppError';}
}class ValidationError extends AppError {constructor(message, field) {super(message, 'VALIDATION_ERROR', 400);this.field = field;}
}class NetworkError extends AppError {constructor(message) {super(message, 'NETWORK_ERROR', 0);}
}// 错误处理中心
class ErrorHandler {static handle(error) {if (error instanceof ValidationError) {showFieldError(error.field, error.message);} else if (error instanceof NetworkError) {showNetworkError();} else if (error instanceof AppError) {showError(error.message);} else {console.error('未知错误:', error);showError('系统错误,请稍后重试');}}
}

3. 统一的 API 错误处理

// api.js
class ApiClient {constructor(baseURL) {this.baseURL = baseURL;}async request(endpoint, options = {}) {const url = `${this.baseURL}${endpoint}`;const [error, response] = await ErrorUtils.to(ErrorUtils.withTimeout(fetch(url, {headers: {'Content-Type': 'application/json',...options.headers,},...options,}),10000));if (error) {throw new NetworkError('网络请求失败');}if (!response.ok) {const errorData = await response.json().catch(() => ({}));switch (response.status) {case 400:throw new ValidationError(errorData.message || '请求参数错误');case 401:throw new AppError('未授权访问', 'UNAUTHORIZED', 401);case 403:throw new AppError('权限不足', 'FORBIDDEN', 403);case 404:throw new AppError('资源不存在', 'NOT_FOUND', 404);default:throw new AppError('服务器错误', 'SERVER_ERROR', response.status);}}return response.json();}get(endpoint, options) {return this.request(endpoint, { method: 'GET', ...options });}post(endpoint, data, options) {return this.request(endpoint, {method: 'POST',body: JSON.stringify(data),...options,});}
}const api = new ApiClient('/api');// 使用示例
async function createUser(userData) {const [error, user] = await ErrorUtils.to(api.post('/users', userData));if (error) {ErrorHandler.handle(error);return null;}return user;
}

🔧 工具推荐

NPM 包

  1. await-to-js - 最受欢迎的 to() 函数实现
  2. neverthrow - Result 类型的完整实现
  3. p-retry - 专业的重试工具
  4. p-timeout - Promise 超时处理

IDE 插件

  1. Error Lens - VS Code 错误高亮显示
  2. ESLint - 配置 async/await 相关规则

📚 总结

优雅的 async/await 错误处理不仅能让代码更简洁,还能提高代码的可维护性和可读性。选择合适的模式,结合项目的实际需求,可以大大提升开发效率。

推荐方案选择

  • 新项目:优先使用 Result 包装器模式
  • 现有项目改造:使用 to() 函数渐进式改进
  • 团队协作:统一使用工具函数库
  • 复杂应用:结合多种模式,分场景使用

记住,最好的错误处理方案是团队都能理解和维护的方案。选择适合你项目的模式,保持一致性,让代码更加优雅!

🌟 更多实际应用场景

1. 电商购物车场景

// 购物车操作的复杂错误处理
class ShoppingCartService {constructor(apiClient) {this.api = apiClient;}// 添加商品到购物车async addToCart(productId, quantity = 1) {// 1. 验证商品库存const [stockError, stockInfo] = await ErrorUtils.to(this.api.get(`/products/${productId}/stock`));if (stockError) {throw new AppError('无法获取商品库存信息', 'STOCK_CHECK_FAILED');}if (stockInfo.available < quantity) {throw new ValidationError('库存不足', 'quantity');}// 2. 检查用户购物车容量const [cartError, currentCart] = await ErrorUtils.to(this.api.get('/cart'));if (cartError) {throw new AppError('无法获取购物车信息', 'CART_FETCH_FAILED');}if (currentCart.items.length >= 50) {throw new ValidationError('购物车商品数量已达上限', 'cart_limit');}// 3. 添加商品const [addError, result] = await ErrorUtils.to(this.api.post('/cart/items', {productId,quantity,}));if (addError) {if (addError.code === 'PRODUCT_UNAVAILABLE') {throw new AppError('商品已下架', 'PRODUCT_UNAVAILABLE');}throw new AppError('添加失败,请重试', 'ADD_TO_CART_FAILED');}return result;}// 批量更新购物车async batchUpdateCart(updates) {const results = [];const failures = [];for (const update of updates) {const [error, result] = await ErrorUtils.to(this.updateCartItem(update.itemId, update.quantity));if (error) {failures.push({itemId: update.itemId,error: error.message,});} else {results.push(result);}}return {success: results,failures,hasFailures: failures.length > 0,};}async updateCartItem(itemId, quantity) {if (quantity <= 0) {return this.api.delete(`/cart/items/${itemId}`);}return this.api.put(`/cart/items/${itemId}`, { quantity });}
}// 使用示例
const cartService = new ShoppingCartService(api);async function handleAddToCart(productId, quantity) {const [error, result] = await ErrorUtils.to(cartService.addToCart(productId, quantity));if (error) {switch (error.code) {case 'STOCK_CHECK_FAILED':showMessage('网络异常,请稍后重试', 'error');break;case 'PRODUCT_UNAVAILABLE':showMessage('商品已售罄', 'warning');break;case 'ADD_TO_CART_FAILED':showMessage('添加失败,请重试', 'error');break;default:if (error instanceof ValidationError) {showFieldError(error.field, error.message);} else {showMessage('操作失败', 'error');}}return;}showMessage('添加成功', 'success');updateCartBadge(result.totalItems);
}

2. 文件上传进度场景

// 文件上传的错误处理和进度跟踪
class FileUploadService {constructor() {this.activeUploads = new Map();}async uploadFile(file, options = {}) {const uploadId = this.generateUploadId();const {onProgress,onSuccess,onError,maxSize = 50 * 1024 * 1024, // 50MBallowedTypes = ['image/*', 'application/pdf'],} = options;// 文件验证const validationResult = this.validateFile(file, { maxSize, allowedTypes });if (!validationResult.valid) {const error = new ValidationError(validationResult.message, 'file');onError?.(error);return Result.error(error);}try {// 1. 获取上传凭证const [credError, credentials] = await ErrorUtils.to(this.getUploadCredentials(file.name, file.size, file.type));if (credError) {throw new AppError('获取上传凭证失败', 'CREDENTIALS_FAILED');}// 2. 分片上传const chunks = this.createFileChunks(file);const uploadedChunks = [];this.activeUploads.set(uploadId, {file,chunks: chunks.length,uploaded: 0,status: 'uploading',});for (let i = 0; i < chunks.length; i++) {const chunk = chunks[i];// 重试机制const [chunkError, chunkResult] = await ErrorUtils.retry(() => this.uploadChunk(credentials.uploadUrl, chunk, i),3,1000);if (chunkError) {this.activeUploads.delete(uploadId);throw new AppError(`分片 ${i + 1} 上传失败`, 'CHUNK_UPLOAD_FAILED');}uploadedChunks.push(chunkResult);// 更新进度const progress = Math.round(((i + 1) / chunks.length) * 100);onProgress?.({progress,uploadId,chunk: i + 1,total: chunks.length,});this.activeUploads.get(uploadId).uploaded = i + 1;}// 3. 合并文件const [mergeError, finalResult] = await ErrorUtils.to(this.mergeChunks(credentials.fileId, uploadedChunks));if (mergeError) {throw new AppError('文件合并失败', 'MERGE_FAILED');}this.activeUploads.delete(uploadId);onSuccess?.(finalResult);return Result.ok(finalResult);} catch (error) {this.activeUploads.delete(uploadId);onError?.(error);return Result.error(error);}}validateFile(file, { maxSize, allowedTypes }) {if (!file) {return { valid: false, message: '请选择文件' };}if (file.size > maxSize) {const sizeMB = Math.round(maxSize / 1024 / 1024);return { valid: false, message: `文件大小不能超过 ${sizeMB}MB` };}const isValidType = allowedTypes.some((type) => {if (type.endsWith('/*')) {return file.type.startsWith(type.slice(0, -1));}return file.type === type;});if (!isValidType) {return { valid: false, message: '文件类型不支持' };}return { valid: true };}async getUploadCredentials(fileName, fileSize, fileType) {return api.post('/upload/credentials', {fileName,fileSize,fileType,});}createFileChunks(file, chunkSize = 2 * 1024 * 1024) {// 2MB per chunkconst chunks = [];let start = 0;while (start < file.size) {const end = Math.min(start + chunkSize, file.size);chunks.push(file.slice(start, end));start = end;}return chunks;}async uploadChunk(uploadUrl, chunk, index) {const formData = new FormData();formData.append('chunk', chunk);formData.append('index', index);return fetch(uploadUrl, {method: 'POST',body: formData,}).then((r) => r.json());}async mergeChunks(fileId, chunks) {return api.post(`/upload/${fileId}/merge`, {chunks: chunks.map((c) => c.chunkId),});}generateUploadId() {return `upload_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;}// 取消上传cancelUpload(uploadId) {if (this.activeUploads.has(uploadId)) {this.activeUploads.get(uploadId).status = 'cancelled';this.activeUploads.delete(uploadId);}}// 获取上传状态getUploadStatus(uploadId) {return this.activeUploads.get(uploadId) || null;}
}// Vue 3 组合式函数
export function useFileUpload() {const uploadService = new FileUploadService();const uploads = ref(new Map());const uploadFile = async (file, options = {}) => {const uploadId = `upload_${Date.now()}`;uploads.value.set(uploadId, {file,progress: 0,status: 'uploading',error: null,result: null,});const result = await uploadService.uploadFile(file, {...options,onProgress: ({ progress }) => {const upload = uploads.value.get(uploadId);if (upload) {upload.progress = progress;}},onSuccess: (result) => {const upload = uploads.value.get(uploadId);if (upload) {upload.status = 'completed';upload.result = result;}},onError: (error) => {const upload = uploads.value.get(uploadId);if (upload) {upload.status = 'error';upload.error = error;}},});return { uploadId, result };};const cancelUpload = (uploadId) => {uploadService.cancelUpload(uploadId);uploads.value.delete(uploadId);};return {uploads: readonly(uploads),uploadFile,cancelUpload,};
}

3. 数据同步和缓存场景

// 复杂的数据同步和缓存错误处理
class DataSyncService {constructor() {this.cache = new Map();this.syncQueue = [];this.isSyncing = false;this.retryAttempts = new Map();}// 获取数据(带缓存)async getData(key, fetcher, options = {}) {const {cacheTime = 5 * 60 * 1000, // 5分钟缓存forceRefresh = false,fallbackData = null,} = options;// 检查缓存if (!forceRefresh && this.cache.has(key)) {const cached = this.cache.get(key);if (Date.now() - cached.timestamp < cacheTime) {return Result.ok(cached.data);}}// 获取新数据const [error, data] = await ErrorUtils.to(fetcher());if (error) {// 如果有缓存数据,返回缓存if (this.cache.has(key)) {const cached = this.cache.get(key);console.warn(`获取 ${key} 失败,使用缓存数据:`, error);return Result.ok(cached.data);}// 如果有降级数据,返回降级数据if (fallbackData !== null) {console.warn(`获取 ${key} 失败,使用降级数据:`, error);return Result.ok(fallbackData);}return Result.error(error);}// 更新缓存this.cache.set(key, {data,timestamp: Date.now(),});return Result.ok(data);}// 离线数据同步async syncOfflineData() {if (this.isSyncing) {return { success: false, message: '正在同步中' };}this.isSyncing = true;const results = {success: [],failed: [],skipped: [],};try {// 检查网络状态if (!navigator.onLine) {throw new AppError('网络不可用', 'NETWORK_UNAVAILABLE');}// 处理同步队列while (this.syncQueue.length > 0) {const item = this.syncQueue.shift();const retryKey = `${item.type}_${item.id}`;const retryCount = this.retryAttempts.get(retryKey) || 0;// 超过重试次数,跳过if (retryCount >= 3) {results.skipped.push({...item,reason: '超过最大重试次数',});this.retryAttempts.delete(retryKey);continue;}const [error, result] = await ErrorUtils.to(this.syncSingleItem(item));if (error) {// 增加重试次数this.retryAttempts.set(retryKey, retryCount + 1);// 如果是临时性错误,重新加入队列if (this.isRetryableError(error)) {this.syncQueue.push(item);}results.failed.push({...item,error: error.message,retryCount: retryCount + 1,});} else {results.success.push({...item,result,});this.retryAttempts.delete(retryKey);}// 避免过快的请求await new Promise((resolve) => setTimeout(resolve, 100));}} finally {this.isSyncing = false;}return results;}async syncSingleItem(item) {switch (item.type) {case 'user_preference':return api.put('/user/preferences', item.data);case 'analytics_event':return api.post('/analytics/events', item.data);case 'draft_save':return api.post('/drafts', item.data);default:throw new AppError(`未知的同步类型: ${item.type}`, 'UNKNOWN_SYNC_TYPE');}}isRetryableError(error) {// 网络错误、服务器临时错误等可重试return (error instanceof NetworkError ||(error.statusCode >= 500 && error.statusCode < 600) ||error.statusCode === 429); // 限流}// 添加到同步队列addToSyncQueue(type, id, data) {this.syncQueue.push({type,id,data,timestamp: Date.now(),});}// 清理过期缓存cleanExpiredCache(maxAge = 30 * 60 * 1000) {// 30分钟const now = Date.now();for (const [key, value] of this.cache.entries()) {if (now - value.timestamp > maxAge) {this.cache.delete(key);}}}
}// 使用示例
const syncService = new DataSyncService();// 获取用户数据(带缓存和降级)
async function fetchUserProfile(userId) {const result = await syncService.getData(`user_profile_${userId}`,() => api.get(`/users/${userId}`),{cacheTime: 10 * 60 * 1000, // 10分钟缓存fallbackData: {id: userId,name: '用户',avatar: '/default-avatar.png',},});if (result.isError()) {console.error('获取用户资料失败:', result.error);return null;}return result.data;
}// 离线操作处理
async function saveUserPreference(key, value) {const [error, result] = await ErrorUtils.to(api.put('/user/preferences', { [key]: value }));if (error) {// 离线时保存到同步队列if (!navigator.onLine || error instanceof NetworkError) {syncService.addToSyncQueue('user_preference', key, { [key]: value });console.log('已添加到离线同步队列');return true;}throw error;}return result;
}

4. 实时数据流处理场景

// WebSocket 连接和实时数据的错误处理
class RealtimeDataService {constructor(url) {this.url = url;this.ws = null;this.reconnectAttempts = 0;this.maxReconnectAttempts = 5;this.reconnectDelay = 1000;this.listeners = new Map();this.messageQueue = [];this.isConnecting = false;}async connect() {if (this.isConnecting) {return;}this.isConnecting = true;try {await this.createConnection();this.reconnectAttempts = 0;this.isConnecting = false;} catch (error) {this.isConnecting = false;throw error;}}async createConnection() {return new Promise((resolve, reject) => {try {this.ws = new WebSocket(this.url);this.ws.onopen = () => {console.log('WebSocket 连接成功');this.processMessageQueue();resolve();};this.ws.onmessage = (event) => {this.handleMessage(event.data);};this.ws.onclose = (event) => {console.log('WebSocket 连接关闭:', event.code, event.reason);this.handleReconnect();};this.ws.onerror = (error) => {console.error('WebSocket 错误:', error);reject(new NetworkError('WebSocket 连接失败'));};// 连接超时setTimeout(() => {if (this.ws.readyState === WebSocket.CONNECTING) {this.ws.close();reject(new NetworkError('WebSocket 连接超时'));}}, 10000);} catch (error) {reject(error);}});}async handleReconnect() {if (this.reconnectAttempts >= this.maxReconnectAttempts) {this.emit('maxReconnectAttemptsReached');return;}this.reconnectAttempts++;const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);console.log(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts}),${delay}ms 后重试`);setTimeout(async () => {const [error] = await ErrorUtils.to(this.connect());if (error) {console.error('重连失败:', error);this.handleReconnect();}}, delay);}handleMessage(data) {try {const message = JSON.parse(data);this.emit('message', message);// 根据消息类型分发if (message.type) {this.emit(message.type, message.payload);}} catch (error) {console.error('解析消息失败:', error, data);this.emit('parseError', { error, rawData: data });}}send(data) {if (this.ws?.readyState === WebSocket.OPEN) {this.ws.send(JSON.stringify(data));return true;} else {// 连接未就绪时加入队列this.messageQueue.push(data);// 尝试连接if (!this.isConnecting) {this.connect().catch((error) => {console.error('发送消息时连接失败:', error);});}return false;}}processMessageQueue() {while (this.messageQueue.length > 0) {const message = this.messageQueue.shift();this.send(message);}}on(event, listener) {if (!this.listeners.has(event)) {this.listeners.set(event, new Set());}this.listeners.get(event).add(listener);}off(event, listener) {if (this.listeners.has(event)) {this.listeners.get(event).delete(listener);}}emit(event, data) {if (this.listeners.has(event)) {this.listeners.get(event).forEach((listener) => {try {listener(data);} catch (error) {console.error(`事件监听器 ${event} 执行失败:`, error);}});}}disconnect() {if (this.ws) {this.ws.close();this.ws = null;}this.reconnectAttempts = this.maxReconnectAttempts; // 阻止重连}
}// 实时数据管理器
class LiveDataManager {constructor() {this.connections = new Map();this.subscriptions = new Map();}async subscribe(channel, handler, options = {}) {const { autoReconnect = true, maxRetries = 5, retryDelay = 1000 } = options;let connection = this.connections.get(channel);if (!connection) {connection = new RealtimeDataService(`wss://api.example.com/live/${channel}`);this.connections.set(channel, connection);// 设置错误处理connection.on('maxReconnectAttemptsReached', () => {this.handleConnectionFailed(channel);});connection.on('parseError', ({ error, rawData }) => {console.error(`频道 ${channel} 消息解析错误:`, error);});}// 添加订阅处理器const subscriptionId = `${channel}_${Date.now()}_${Math.random()}`;this.subscriptions.set(subscriptionId, {channel,handler,connection,});connection.on('message', handler);// 连接const [connectError] = await ErrorUtils.to(connection.connect());if (connectError) {this.subscriptions.delete(subscriptionId);throw new AppError(`订阅频道 ${channel} 失败`, 'SUBSCRIPTION_FAILED');}return subscriptionId;}unsubscribe(subscriptionId) {const subscription = this.subscriptions.get(subscriptionId);if (subscription) {subscription.connection.off('message', subscription.handler);this.subscriptions.delete(subscriptionId);// 如果没有其他订阅,关闭连接const hasOtherSubscriptions = Array.from(this.subscriptions.values()).some((sub) => sub.channel === subscription.channel);if (!hasOtherSubscriptions) {subscription.connection.disconnect();this.connections.delete(subscription.channel);}}}handleConnectionFailed(channel) {console.error(`频道 ${channel} 连接失败,已达到最大重试次数`);// 通知所有该频道的订阅者const channelSubscriptions = Array.from(this.subscriptions.values()).filter((sub) => sub.channel === channel);channelSubscriptions.forEach((sub) => {try {sub.handler({type: 'connection_failed',channel,message: '连接失败,请稍后重试',});} catch (error) {console.error('通知订阅者连接失败时出错:', error);}});}// 获取连接状态getConnectionStatus(channel) {const connection = this.connections.get(channel);if (!connection || !connection.ws) {return 'disconnected';}switch (connection.ws.readyState) {case WebSocket.CONNECTING:return 'connecting';case WebSocket.OPEN:return 'connected';case WebSocket.CLOSING:return 'closing';case WebSocket.CLOSED:return 'closed';default:return 'unknown';}}
}// Vue 3 实时数据 Hook
export function useLiveData(channel, options = {}) {const dataManager = new LiveDataManager();const data = ref(null);const error = ref(null);const status = ref('disconnected');const subscriptionId = ref(null);const subscribe = async () => {try {error.value = null;status.value = 'connecting';const id = await dataManager.subscribe(channel,(message) => {if (message.type === 'connection_failed') {error.value = new Error(message.message);status.value = 'error';} else {data.value = message;status.value = 'connected';}},options);subscriptionId.value = id;status.value = 'connected';} catch (err) {error.value = err;status.value = 'error';}};const unsubscribe = () => {if (subscriptionId.value) {dataManager.unsubscribe(subscriptionId.value);subscriptionId.value = null;status.value = 'disconnected';}};onMounted(() => {subscribe();});onUnmounted(() => {unsubscribe();});return {data: readonly(data),error: readonly(error),status: readonly(status),subscribe,unsubscribe,reconnect: subscribe,};
}

5. 表单验证和提交场景

// 复杂表单的错误处理
class FormValidator {constructor() {this.rules = new Map();this.errors = new Map();}addRule(field, validator, message) {if (!this.rules.has(field)) {this.rules.set(field, []);}this.rules.get(field).push({ validator, message });return this;}async validate(data) {this.errors.clear();const validationPromises = [];for (const [field, rules] of this.rules) {const fieldPromise = this.validateField(field, data[field], data);validationPromises.push(fieldPromise);}await Promise.all(validationPromises);return {valid: this.errors.size === 0,errors: Object.fromEntries(this.errors),};}async validateField(field, value, allData) {const rules = this.rules.get(field) || [];for (const rule of rules) {try {const isValid = await rule.validator(value, allData);if (!isValid) {this.errors.set(field, rule.message);break; // 只显示第一个错误}} catch (error) {this.errors.set(field, `验证失败: ${error.message}`);break;}}}clearErrors() {this.errors.clear();}getFieldError(field) {return this.errors.get(field);}
}// 表单提交服务
class FormSubmissionService {constructor() {this.submitting = new Set();}async submitForm(formId, data, options = {}) {if (this.submitting.has(formId)) {throw new AppError('表单正在提交中', 'FORM_SUBMITTING');}const {validator,beforeSubmit,afterSubmit,onSuccess,onError,endpoint,method = 'POST',} = options;this.submitting.add(formId);try {// 1. 表单验证if (validator) {const validation = await validator.validate(data);if (!validation.valid) {throw new ValidationError('表单验证失败', validation.errors);}}// 2. 提交前处理let processedData = data;if (beforeSubmit) {const [beforeError, result] = await ErrorUtils.to(beforeSubmit(data));if (beforeError) {throw new AppError('提交前处理失败', 'BEFORE_SUBMIT_FAILED');}processedData = result || data;}// 3. 提交数据const [submitError, response] = await ErrorUtils.to(api.request(endpoint, {method,body: JSON.stringify(processedData),}));if (submitError) {// 处理特定的提交错误if (submitError.statusCode === 422) {// 服务器验证错误const validationErrors = submitError.data?.errors || {};throw new ValidationError('服务器验证失败', validationErrors);}throw new AppError('提交失败,请重试', 'SUBMIT_FAILED');}// 4. 提交后处理if (afterSubmit) {await ErrorUtils.to(afterSubmit(response));}onSuccess?.(response);return Result.ok(response);} catch (error) {onError?.(error);return Result.error(error);} finally {this.submitting.delete(formId);}}isSubmitting(formId) {return this.submitting.has(formId);}
}// 使用示例:用户注册表单
async function setupUserRegistrationForm() {const validator = new FormValidator().addRule('username',async (value) => {if (!value || value.length < 3) return false;// 检查用户名是否已存在const [error, result] = await ErrorUtils.to(api.get(`/users/check-username?username=${encodeURIComponent(value)}`));return !error && result.available;},'用户名至少3位且不能重复').addRule('email',(value) => {const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;return emailRegex.test(value);},'请输入有效的邮箱地址').addRule('password',(value) => {return (value &&value.length >= 8 &&/[A-Z]/.test(value) &&/[0-9]/.test(value));},'密码至少8位,包含大写字母和数字').addRule('confirmPassword',(value, allData) => {return value === allData.password;},'两次密码输入不一致');const formService = new FormSubmissionService();const submitRegistration = async (formData) => {const result = await formService.submitForm('registration', formData, {validator,endpoint: '/users/register',beforeSubmit: async (data) => {// 添加时间戳和设备信息return {...data,timestamp: Date.now(),deviceInfo: {userAgent: navigator.userAgent,timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,},};},afterSubmit: async (response) => {// 注册成功后的处理if (response.user) {localStorage.setItem('user', JSON.stringify(response.user));// 发送欢迎邮件await ErrorUtils.to(api.post('/users/send-welcome-email', { userId: response.user.id }));}},onSuccess: (response) => {showMessage('注册成功!', 'success');router.push('/dashboard');},onError: (error) => {if (error instanceof ValidationError) {// 显示字段验证错误Object.entries(error.field || {}).forEach(([field, message]) => {showFieldError(field, message);});} else {showMessage(error.message || '注册失败,请重试', 'error');}},});return result;};return {validator,submitRegistration,isSubmitting: (formId) => formService.isSubmitting(formId),};
}

🚀 性能优化和测试实例

1. 性能监控和错误追踪

// 性能监控的错误处理
class PerformanceMonitor {constructor() {this.metrics = new Map();this.errorTracker = new Map();}// 监控异步操作性能async monitorAsyncOperation(name, operation, options = {}) {const {timeout = 30000,expectSuccessRate = 0.95,alertThreshold = 1000,} = options;const startTime = performance.now();const operationId = `${name}_${Date.now()}`;try {// 添加超时控制const result = await ErrorUtils.withTimeout(operation(), timeout);const duration = performance.now() - startTime;this.recordSuccess(name, duration);// 性能告警if (duration > alertThreshold) {console.warn(`操作 ${name} 耗时过长: ${duration}ms`);this.reportSlowOperation(name, duration, operationId);}return Result.ok(result);} catch (error) {const duration = performance.now() - startTime;this.recordError(name, error, duration);// 检查错误率const stats = this.getOperationStats(name);if (stats.errorRate > 1 - expectSuccessRate) {this.alertHighErrorRate(name, stats);}return Result.error(error);}}recordSuccess(operationName, duration) {const key = `${operationName}_success`;const records = this.metrics.get(key) || [];records.push({timestamp: Date.now(),duration,status: 'success',});// 只保留最近1000条记录if (records.length > 1000) {records.shift();}this.metrics.set(key, records);}recordError(operationName, error, duration) {const key = `${operationName}_error`;const records = this.errorTracker.get(key) || [];records.push({timestamp: Date.now(),duration,error: {message: error.message,code: error.code,stack: error.stack,type: error.constructor.name,},status: 'error',});if (records.length > 1000) {records.shift();}this.errorTracker.set(key, records);}getOperationStats(operationName) {const successKey = `${operationName}_success`;const errorKey = `${operationName}_error`;const successes = this.metrics.get(successKey) || [];const errors = this.errorTracker.get(errorKey) || [];const total = successes.length + errors.length;const errorRate = total > 0 ? errors.length / total : 0;const durations = successes.map((r) => r.duration);const avgDuration =durations.length > 0? durations.reduce((a, b) => a + b, 0) / durations.length: 0;const p95Duration =durations.length > 0? durations.sort((a, b) => a - b)[Math.floor(durations.length * 0.95)]: 0;return {total,successes: successes.length,errors: errors.length,errorRate,avgDuration,p95Duration,recentErrors: errors.slice(-5), // 最近5个错误};}async reportSlowOperation(name, duration, operationId) {// 上报慢操作const [error] = await ErrorUtils.to(api.post('/monitoring/slow-operations', {operationName: name,duration,operationId,timestamp: Date.now(),userAgent: navigator.userAgent,url: window.location.href,}));if (error) {console.warn('上报慢操作失败:', error);}}async alertHighErrorRate(name, stats) {console.error(`操作 ${name} 错误率过高:`, stats);// 发送告警const [error] = await ErrorUtils.to(api.post('/monitoring/alerts', {type: 'high_error_rate',operationName: name,errorRate: stats.errorRate,recentErrors: stats.recentErrors,timestamp: Date.now(),}));if (error) {console.warn('发送告警失败:', error);}}// 生成性能报告generateReport() {const operations = new Set();// 收集所有操作名称this.metrics.forEach((_, key) => {const operationName = key.replace('_success', '');operations.add(operationName);});this.errorTracker.forEach((_, key) => {const operationName = key.replace('_error', '');operations.add(operationName);});const report = {};operations.forEach((name) => {report[name] = this.getOperationStats(name);});return report;}
}// 全局性能监控实例
const performanceMonitor = new PerformanceMonitor();// 装饰器:自动性能监控
function withPerformanceMonitoring(operationName, options = {}) {return function (target, propertyKey, descriptor) {const originalMethod = descriptor.value;descriptor.value = async function (...args) {return await performanceMonitor.monitorAsyncOperation(`${target.constructor.name}.${propertyKey}`,() => originalMethod.apply(this, args),options);};return descriptor;};
}// 使用示例
class DataService {@withPerformanceMonitoring('fetchUserData', {timeout: 5000,alertThreshold: 2000,})async fetchUserData(userId) {const response = await fetch(`/api/users/${userId}`);return response.json();}@withPerformanceMonitoring('batchUpdateUsers', {timeout: 30000,expectSuccessRate: 0.98,})async batchUpdateUsers(updates) {const response = await fetch('/api/users/batch', {method: 'PUT',body: JSON.stringify(updates),});return response.json();}
}

2. 错误处理的单元测试

// 测试工具类
class AsyncTestUtils {// 模拟异步错误static async simulateAsyncError(errorType = 'network', delay = 100) {await new Promise((resolve) => setTimeout(resolve, delay));switch (errorType) {case 'network':throw new NetworkError('模拟网络错误');case 'validation':throw new ValidationError('模拟验证错误', 'testField');case 'timeout':throw new Error('操作超时');default:throw new Error('模拟通用错误');}}// 模拟成功响应static async simulateAsyncSuccess(data = { success: true }, delay = 100) {await new Promise((resolve) => setTimeout(resolve, delay));return data;}// 模拟间歇性失败static async simulateIntermittentFailure(successRate = 0.7) {const random = Math.random();if (random < successRate) {return this.simulateAsyncSuccess();} else {return this.simulateAsyncError();}}
}// Jest 测试示例
describe('ErrorUtils.to() 函数测试', () => {test('应该正确处理成功的 Promise', async () => {const [error, data] = await ErrorUtils.to(AsyncTestUtils.simulateAsyncSuccess({ value: 42 }));expect(error).toBeNull();expect(data).toEqual({ value: 42 });});test('应该正确处理失败的 Promise', async () => {const [error, data] = await ErrorUtils.to(AsyncTestUtils.simulateAsyncError('network'));expect(error).toBeInstanceOf(NetworkError);expect(data).toBeNull();});test('应该处理超时错误', async () => {const timeoutPromise = new Promise((_, reject) => {setTimeout(() => reject(new Error('超时')), 50);});const [error, data] = await ErrorUtils.to(timeoutPromise);expect(error).toBeInstanceOf(Error);expect(error.message).toBe('超时');expect(data).toBeNull();});
});describe('ErrorUtils.retry() 重试功能测试', () => {test('应该在最终成功时返回结果', async () => {let attempts = 0;const flaky = async () => {attempts++;if (attempts < 3) {throw new Error('失败');}return { success: true, attempts };};const [error, result] = await ErrorUtils.retry(flaky, 3, 10);expect(error).toBeNull();expect(result).toEqual({ success: true, attempts: 3 });});test('应该在达到最大重试次数后返回错误', async () => {const alwaysFails = async () => {throw new Error('总是失败');};const [error, result] = await ErrorUtils.retry(alwaysFails, 2, 10);expect(error).toBeInstanceOf(Error);expect(error.message).toBe('总是失败');expect(result).toBeNull();});
});describe('Result 包装器测试', () => {test('Result.ok() 应该创建成功结果', () => {const result = Result.ok('test data');expect(result.isOk()).toBe(true);expect(result.isError()).toBe(false);expect(result.unwrap()).toBe('test data');});test('Result.error() 应该创建错误结果', () => {const error = new Error('test error');const result = Result.error(error);expect(result.isOk()).toBe(false);expect(result.isError()).toBe(true);expect(() => result.unwrap()).toThrow('test error');});test('Result.from() 应该正确包装 Promise', async () => {const successResult = await Result.from(AsyncTestUtils.simulateAsyncSuccess('success'));expect(successResult.isOk()).toBe(true);expect(successResult.unwrap()).toBe('success');const errorResult = await Result.from(AsyncTestUtils.simulateAsyncError());expect(errorResult.isError()).toBe(true);});test('map() 应该正确转换成功值', () => {const result = Result.ok(5);const mapped = result.map((x) => x * 2);expect(mapped.unwrap()).toBe(10);});test('map() 应该跳过错误值', () => {const result = Result.error(new Error('test'));const mapped = result.map((x) => x * 2);expect(mapped.isError()).toBe(true);});
});

3. 集成测试和 E2E 测试

// Cypress E2E 测试示例
describe('错误处理集成测试', () => {beforeEach(() => {// 模拟网络错误cy.intercept('GET', '/api/users/*', { forceNetworkError: true }).as('userError');cy.visit('/user-profile');});it('应该优雅地处理网络错误', () => {// 等待错误处理cy.wait('@userError');// 验证错误消息显示cy.contains('网络异常,请稍后重试').should('be.visible');// 验证重试按钮可用cy.get('[data-testid="retry-button"]').should('be.enabled');// 验证降级内容显示cy.get('[data-testid="fallback-content"]').should('be.visible');});it('应该在网络恢复后自动重试', () => {// 模拟网络恢复cy.intercept('GET', '/api/users/*', { fixture: 'user.json' }).as('userSuccess');// 点击重试cy.get('[data-testid="retry-button"]').click();cy.wait('@userSuccess');// 验证数据正确显示cy.get('[data-testid="user-name"]').should('contain', 'John Doe');cy.get('[data-testid="error-message"]').should('not.exist');});
});// Playwright 测试示例
import { test, expect } from '@playwright/test';test.describe('异步错误处理', () => {test('文件上传错误处理', async ({ page }) => {await page.goto('/upload');// 模拟服务器错误await page.route('/api/upload/**', (route) => {route.fulfill({status: 500,body: JSON.stringify({ error: '服务器内部错误' }),});});// 上传文件const fileInput = page.locator('input[type="file"]');await fileInput.setInputFiles('./test-files/sample.pdf');// 验证错误处理await expect(page.locator('[data-testid="upload-error"]')).toBeVisible();await expect(page.locator('[data-testid="upload-error"]')).toContainText('上传失败');// 验证重试功能await page.locator('[data-testid="retry-upload"]').click();await expect(page.locator('[data-testid="upload-progress"]')).toBeVisible();});test('实时数据连接错误处理', async ({ page }) => {// 模拟 WebSocket 连接失败await page.addInitScript(() => {window.WebSocket = class {constructor() {setTimeout(() => {this.onerror && this.onerror(new Event('error'));}, 100);}};});await page.goto('/dashboard');// 验证错误状态显示await expect(page.locator('[data-testid="connection-status"]')).toContainText('连接失败');await expect(page.locator('[data-testid="reconnect-button"]')).toBeVisible();// 验证离线模式await expect(page.locator('[data-testid="offline-indicator"]')).toBeVisible();});
});

4. 性能基准测试

// 性能基准测试
class PerformanceBenchmark {static async benchmarkErrorHandling() {const iterations = 1000;const results = {};// 测试传统 try-catchconsole.time('traditional-try-catch');for (let i = 0; i < iterations; i++) {try {await AsyncTestUtils.simulateIntermittentFailure(0.8);} catch (error) {// 处理错误}}console.timeEnd('traditional-try-catch');// 测试 to() 函数console.time('to-function');for (let i = 0; i < iterations; i++) {const [error, result] = await ErrorUtils.to(AsyncTestUtils.simulateIntermittentFailure(0.8));}console.timeEnd('to-function');// 测试 Result 包装器console.time('result-wrapper');for (let i = 0; i < iterations; i++) {const result = await Result.from(AsyncTestUtils.simulateIntermittentFailure(0.8));}console.timeEnd('result-wrapper');// 内存使用情况if (performance.memory) {results.memoryUsage = {used: performance.memory.usedJSHeapSize,total: performance.memory.totalJSHeapSize,limit: performance.memory.jsHeapSizeLimit,};}return results;}static async stressTest() {const concurrentOperations = 100;const operations = Array.from({ length: concurrentOperations }, (_, i) =>ErrorUtils.to(AsyncTestUtils.simulateIntermittentFailure(0.7)));const startTime = performance.now();const results = await Promise.all(operations);const endTime = performance.now();const successes = results.filter(([error]) => !error).length;const failures = results.filter(([error]) => error).length;return {duration: endTime - startTime,totalOperations: concurrentOperations,successes,failures,successRate: successes / concurrentOperations,opsPerSecond: concurrentOperations / ((endTime - startTime) / 1000),};}
}// 运行基准测试
async function runBenchmarks() {console.log('开始性能基准测试...');const errorHandlingBenchmark =await PerformanceBenchmark.benchmarkErrorHandling();console.log('错误处理基准测试结果:', errorHandlingBenchmark);const stressTestResult = await PerformanceBenchmark.stressTest();console.log('压力测试结果:', stressTestResult);// 生成报告const report = {timestamp: new Date().toISOString(),userAgent: navigator.userAgent,errorHandling: errorHandlingBenchmark,stressTest: stressTestResult,};// 保存或上传报告localStorage.setItem('performance-report', JSON.stringify(report));return report;
}

5. 错误边界和全局错误处理

// React 错误边界
class AsyncErrorBoundary extends React.Component {constructor(props) {super(props);this.state = { hasError: false, error: null, errorInfo: null };}static getDerivedStateFromError(error) {return { hasError: true };}componentDidCatch(error, errorInfo) {this.setState({error,errorInfo,});// 异步错误上报this.reportError(error, errorInfo);}async reportError(error, errorInfo) {const [reportError] = await ErrorUtils.to(api.post('/errors/report', {message: error.message,stack: error.stack,componentStack: errorInfo.componentStack,timestamp: Date.now(),url: window.location.href,userAgent: navigator.userAgent,}));if (reportError) {console.warn('错误上报失败:', reportError);}}render() {if (this.state.hasError) {return (this.props.fallback || (<div className="error-boundary"><h2>出现了一些问题</h2><details style={{ whiteSpace: 'pre-wrap' }}>{this.state.error && this.state.error.toString()}<br />{this.state.errorInfo.componentStack}</details><buttononClick={() => this.setState({ hasError: false, error: null })}>重试</button></div>));}return this.props.children;}
}// Vue 3 全局错误处理
const app = createApp(App);app.config.errorHandler = async (error, instance, info) => {console.error('Vue 全局错误:', error, info);// 异步错误上报const [reportError] = await ErrorUtils.to(api.post('/errors/vue-report', {message: error.message,stack: error.stack,componentName: instance?.$options.name || 'Unknown',errorInfo: info,timestamp: Date.now(),route: instance?.$route?.path,}));if (reportError) {console.warn('Vue 错误上报失败:', reportError);}
};// 全局未捕获错误处理
window.addEventListener('error', async (event) => {const [reportError] = await ErrorUtils.to(api.post('/errors/global', {message: event.message,filename: event.filename,lineno: event.lineno,colno: event.colno,stack: event.error?.stack,timestamp: Date.now(),}));if (reportError) {console.warn('全局错误上报失败:', reportError);}
});// 未捕获的 Promise 错误
window.addEventListener('unhandledrejection', async (event) => {console.error('未处理的 Promise 拒绝:', event.reason);const [reportError] = await ErrorUtils.to(api.post('/errors/unhandled-promise', {reason: event.reason?.message || String(event.reason),stack: event.reason?.stack,timestamp: Date.now(),}));if (reportError) {console.warn('Promise 错误上报失败:', reportError);}// 阻止控制台错误输出event.preventDefault();
});

📊 错误处理最佳实践总结

1. 选择指南

场景推荐方案原因
简单异步操作to() 函数语法简洁,易于理解
复杂业务逻辑Result 包装器类型安全,链式操作
重复性操作装饰器模式代码复用,统一处理
表单验证专用验证器业务专一,错误详细
实时数据连接管理器自动重连,状态管理
性能敏感性能监控及时发现,主动优化

2. 性能对比

方案内存开销执行速度代码复杂度可维护性
try-catch
to() 函数
Result 包装器
装饰器模式

3. 团队采用建议

  1. 渐进式采用:从 to() 函数开始,逐步引入其他模式
  2. 统一规范:制定团队编码规范,保持一致性
  3. 工具支持:配置 ESLint 规则,IDE 代码片段
  4. 培训推广:团队分享,代码审查
  5. 监控反馈:收集使用反馈,持续优化

本文档持续更新中,欢迎贡献更多优雅的错误处理模式!

http://www.dtcms.com/a/398055.html

相关文章:

  • 八、神经网络(下)
  • 鲜花购物商城(WebSocket及时通讯、协同过滤算法、支付宝沙盒支付、Echarts图形化分析、快递物流API)
  • 精准调度,确定性保障:信而泰IEEE 802.1Qbv协议测试实战指南
  • 硬件开发_基于STM32单片机的智能路灯系统
  • 学校校园网站建设服务程序员用的编程软件
  • 网站图标ico企业网关路由器怎么设置
  • 怎么安装并使用数字人EchoMimicV2
  • 电气仿真模型资料合集,微电网优化,综合能源调度,配电网无功优化,风光出力预测,电动汽车
  • FunASR:面向产业落地的端到端语音识别系统
  • FFmpeg 深入精讲(四)SDL音视频渲染实践
  • 用“循序渐进+分班教学”提升口齿不清者的语音识别
  • 细致调优Parakeet-TDT:在Speech Accessibility挑战赛中实现口吃语音识别新突破
  • 什么做书籍的网站做淘宝这种网站
  • Spring MVC 请求执行流程详解
  • 德州网站推广尚义网站建设
  • 麒麟 Linux|深入解析 Linux 文件系统架构:理念、结构与工作机制
  • 编程语言综合教程:Java、Python、C++、Go 全面解析
  • 第三部分:VTK过滤器类详解(第58章 图像处理过滤器类)
  • 瑞芯微RK35XX系列Linux实时性详细测试对比( PREEMPT_RT和Xenomai )
  • 虚拟机ubuntu用wifi adb 调试手机
  • 解决慢SQL问题
  • OpenEuler安装mysql二进制版本
  • 【ADB】常用按键代码:adb的所有模拟按键
  • 网站制作基础教程外贸公司开办流程
  • 山亭建设局网站翻译网页
  • 坂田做网站的公司听小说的网站哪个好
  • CNN-Transformer:PyTorch遥感【含无人机】影像的分类、检测、语义分割和点云分类
  • RAG知识库构建
  • MinMaxScaler Scikit-learn sparkml 稀疏向量
  • 基于WPF实现打印机连接与打印功能