JavaScript/TypeScript 异常处理进阶:从装饰器到错误边界
发布时间:2026/11/3 7:00:00 锦皓数字建站

1. 为什么我们需要摆脱手动 try-catch在十多年的编码生涯里我见过太多这样的代码块try { const result someRiskyOperation(); // 业务逻辑代码... } catch (error) { console.error(操作失败:, error); // 可能还有各种错误处理分支 }这种模式的问题在于它把业务逻辑和错误处理强耦合在一起。当项目规模扩大时你会发现代码中遍布着重复的异常处理逻辑核心业务逻辑被错误处理代码淹没不同类型的错误难以统一管理测试用例变得臃肿难维护2. 现代异常处理方案解析2.1 装饰器方案在 TypeScript/JavaScript 生态中装饰器提供了一种优雅的解决方案function errorHandler(target: any, key: string, descriptor: PropertyDescriptor) { const originalMethod descriptor.value; descriptor.value async function(...args: any[]) { try { return await originalMethod.apply(this, args); } catch (error) { // 统一的错误处理逻辑 this.handleError(error); } }; return descriptor; } class APIService { errorHandler async fetchData() { // 业务逻辑... } private handleError(error: Error) { // 集中式错误处理 } }实际项目中我们可以扩展这个装饰器支持错误类型分类处理自动重试机制错误上报集成2.2 函数式方案对于函数式编程风格我们可以实现高阶函数const withErrorHandling (fn) async (...args) { try { return await fn(...args); } catch (error) { // 可以在这里添加上下文信息 error.context { args }; throw error; // 或者返回统一的错误格式 } }; // 使用示例 const safeFetchUser withErrorHandling(async (userId) { // 原始业务逻辑 });这种方案的优点在于保持函数的纯净性方便组合和重用适合函数式编程范式3. 高级错误处理模式3.1 错误分类系统建立清晰的错误类型体系至关重要class AppError extends Error { constructor( public readonly code: string, public readonly details?: Recordstring, any ) { super(); } } class NetworkError extends AppError { constructor(details?: { url: string; status: number }) { super(NETWORK_ERROR, details); } } // 使用示例 throw new NetworkError({ url: /api/users, status: 500 });3.2 错误边界模式借鉴 React 的错误边界概念我们可以实现class ErrorBoundary { static async executeT(fn: () PromiseT): PromiseT | ErrorResponse { try { return await fn(); } catch (error) { if (error instanceof AppError) { return this.handleKnownError(error); } return this.handleUnknownError(error); } } private static handleKnownError(error: AppError) { // 根据错误类型返回特定响应 } }4. 实战中的经验技巧4.1 错误上下文增强在异步操作中错误经常丢失关键上下文。解决方案async function withContext(fn, context) { try { return await fn(); } catch (error) { error.context { ...error.context, ...context }; throw error; } } // 使用示例 await withContext(() fetchUser(userId), { userId, requestId: generateId() });4.2 错误恢复策略对于可恢复错误实现智能重试async function withRetryT( fn: () PromiseT, options: { maxRetries: number; retryDelay: number; shouldRetry: (error: Error) boolean; } ): PromiseT { let attempts 0; while (true) { try { return await fn(); } catch (error) { if (attempts options.maxRetries || !options.shouldRetry(error)) { throw error; } await new Promise(resolve setTimeout(resolve, options.retryDelay) ); attempts; } } }5. 测试策略优化5.1 错误注入测试describe(API Service, () { it(should handle network errors, async () { const mockFetch jest.fn().mockRejectedValue( new NetworkError({ status: 500 }) ); const service new APIService(mockFetch); const result await service.fetchData(); expect(result).toEqual({ success: false, code: NETWORK_ERROR }); }); });5.2 错误覆盖率检查配置 Jest 或其它测试框架确保所有定义的错误类型都被测试用例覆盖每个错误分支都有对应的测试错误消息格式符合规范6. 监控与日志集成6.1 结构化错误日志interface ErrorLog { timestamp: string; code: string; message: string; stack?: string; context?: Recordstring, any; severity: low | medium | high; } function logError(error: Error): ErrorLog { const entry: ErrorLog { timestamp: new Date().toISOString(), message: error.message, severity: medium }; if (error instanceof AppError) { entry.code error.code; entry.context error.details; } // 发送到日志服务 logger.send(entry); return entry; }6.2 错误聚合分析使用 Sentry 或类似工具时配置错误分组规则严重程度自动分类上下文信息自动附加用户反馈收集7. 性能考量错误处理引入的性能开销主要来自调用栈收集上下文序列化日志写入优化建议生产环境限制调用栈深度异步非阻塞的日志写入敏感信息过滤前置8. 渐进式迁移策略对于已有项目推荐迁移路径先在新代码中使用新方案逐步重构高频修改的模块最后处理稳定不变的遗留代码建立代码规范防止倒退关键指标监控未处理错误数量错误处理代码重复率错误解决平均时间
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。