
1. 为什么我们需要封装错误处理在JavaScript开发中错误处理一直是个让人头疼的问题。我见过太多项目因为糟糕的错误处理机制导致线上事故频发也见过不少开发者重复编写几乎相同的try-catch块。特别是在async/await成为主流后错误处理模式变得更加复杂。想象一下这样的场景你的Node.js服务有50个API接口每个接口都需要处理数据库查询错误、参数校验错误和业务逻辑错误。如果不做封装你会在每个路由处理函数中看到这样的代码try { const data await db.query(SELECT...); // 业务逻辑... } catch (err) { if (err instanceof DBError) { res.status(500).json({ error: 数据库错误 }); } else if (err instanceof ValidationError) { res.status(400).json({ error: err.message }); } else { // 其他错误处理... } }这种重复不仅让代码变得臃肿更重要的是当需要修改错误处理逻辑时比如增加错误日志你需要在50个地方做同样的修改。这就是为什么我们需要封装错误处理模式。2. 基础封装方案2.1 同步代码的try-catch封装我们先从最简单的同步代码封装开始。一个常见的模式是创建高阶函数来包裹业务逻辑function withTryCatch(handler) { return function(...args) { try { return handler(...args); } catch (error) { console.error(捕获到同步错误:, error); // 统一的错误处理逻辑 throw error; // 或者返回统一的错误响应 } }; } // 使用示例 const safeHandler withTryCatch(function(data) { if (!data) throw new Error(数据不能为空); // 正常业务逻辑 return processData(data); });这种封装有几个关键优势错误日志集中处理避免在每个catch块中重复console.error可以统一添加错误上报逻辑业务代码更专注于核心逻辑2.2 async/await的错误处理封装对于异步代码我们需要考虑更多情况。最直接的封装方式类似同步版本function withAsyncTryCatch(handler) { return async function(...args) { try { return await handler(...args); } catch (error) { console.error(捕获到异步错误:, error); // 统一的异步错误处理 throw error; } }; }但这样的封装还不够强大。在实际项目中我们通常需要更精细的控制function createAsyncHandler(options {}) { const { errorLogger console.error, shouldRethrow false, transformError err err } options; return function(handler) { return async function(...args) { try { return await handler(...args); } catch (error) { const transformed transformError(error); errorLogger(transformed); if (shouldRethrow) { throw transformed; } return { error: transformed }; } }; }; } // 使用示例 const apiHandler createAsyncHandler({ errorLogger: sentry.captureException, transformError: err ({ code: err.code || 500, message: err.message || 服务器错误 }) }); const safeFetchUser apiHandler(async (userId) { const user await db.user.find(userId); if (!user) throw new Error(用户不存在); return user; });3. 高级封装技巧3.1 错误分类处理在实际项目中不同类型的错误通常需要不同的处理方式。我们可以扩展之前的封装来支持错误分类class AppError extends Error { constructor(message, options {}) { super(message); this.code options.code || GENERIC_ERROR; this.statusCode options.statusCode || 500; this.isOperational options.isOperational ! false; } } class ValidationError extends AppError { constructor(message) { super(message, { code: VALIDATION_ERROR, statusCode: 400 }); } } function createErrorHandler(strategies) { return function(handler) { return async function(...args) { try { return await handler(...args); } catch (error) { const strategy strategies[error.constructor.name] || strategies.default; return strategy(error, ...args); } }; }; } // 使用示例 const handler createErrorHandler({ ValidationError: (err) ({ error: err.message, status: 400 }), DBError: (err) ({ error: 数据库错误, status: 503 }), default: (err) ({ error: 服务器错误, status: 500 }) }); const validateInput handler(async (input) { if (!input.name) throw new ValidationError(名称不能为空); // ... });3.2 上下文感知的错误处理有时候错误处理需要访问调用时的上下文。我们可以修改封装函数来传递上下文function createContextAwareHandler(getContext) { return function(handler) { return async function(...args) { const context getContext(...args); try { return await handler(...args); } catch (error) { // 错误处理可以使用上下文 context.logger.error(error, { context }); if (context.user) { notifyUser(context.user, error); } throw error; } }; }; } // Express中间件示例 const apiHandler createContextAwareHandler((req, res) ({ logger: req.logger, user: req.user, ip: req.ip })); router.post(/users, apiHandler(async (req, res) { // 业务逻辑 }));4. 实战中的最佳实践4.1 Express/Koa中间件封装在Node.js Web框架中我们可以创建错误处理中间件// Express版本 function errorMiddleware(options {}) { return function(err, req, res, next) { const { logger console.error } options; logger(err, { path: req.path, method: req.method, params: req.params, body: req.body }); const status err.statusCode || 500; const response { error: err.message || Internal Server Error, ...(process.env.NODE_ENV development { stack: err.stack }) }; res.status(status).json(response); }; } // Koa版本 async function koaErrorMiddleware(ctx, next) { try { await next(); } catch (err) { ctx.status err.statusCode || 500; ctx.body { error: err.message || Internal Server Error, ...(process.env.NODE_ENV development { stack: err.stack }) }; ctx.app.emit(error, err, ctx); } }4.2 React/Vue组件中的错误边界在前端框架中我们可以实现类似的错误边界模式// React错误边界 class ErrorBoundary extends React.Component { state { error: null }; static getDerivedStateFromError(error) { return { error }; } componentDidCatch(error, info) { this.props.onError?.(error, info); } render() { if (this.state.error) { return this.props.fallback || divSomething went wrong/div; } return this.props.children; } } // 使用示例 ErrorBoundary fallback{ErrorPage /} onError{(error) trackError(error)} UserProfile / /ErrorBoundary5. 性能与调试考量5.1 错误处理对性能的影响虽然错误处理封装带来了很多好处但也需要注意性能影响try-catch的性能开销在现代JavaScript引擎中try-catch块在没有抛出错误时几乎不会影响性能。但一旦错误发生捕获和处理错误的成本会显著增加。错误对象的创建创建Error对象包括自定义错误会涉及堆内存分配和栈追踪收集这在频繁调用的函数中可能成为瓶颈。解决方案对于性能关键路径考虑返回错误对象而不是抛出异常避免在循环内部使用try-catch对于预期会频繁发生的错误如验证失败使用返回错误模式而不是异常5.2 调试技巧封装错误处理可能会使错误堆栈更难追踪。以下是一些调试技巧保留原始堆栈catch (error) { // 添加当前上下文信息但不破坏原始堆栈 const contextualError new Error(处理用户请求失败: ${error.message}); contextualError.stack ${contextualError.stack}\nCaused by: ${error.stack}; throw contextualError; }使用source-map支持确保生产环境错误也能映射回源代码错误ID关联为每个错误生成唯一ID方便前后端日志关联6. 测试策略测试封装后的错误处理逻辑同样重要describe(错误处理封装, () { it(应该捕获同步错误, () { const fn () { throw new Error(test); }; const wrapped withTryCatch(fn); expect(wrapped).not.toThrow(); }); it(应该转换错误类型, async () { const handler createAsyncHandler({ transformError: err new CustomError(err.message) }); const failingAsync handler(async () { throw new Error(original); }); await expect(failingAsync()).rejects.toBeInstanceOf(CustomError); }); it(应该传递上下文, async () { const mockLogger jest.fn(); const contextHandler createContextAwareHandler(() ({ logger: mockLogger })); const failing contextHandler(async () { throw new Error(test); }); await failing(); expect(mockLogger).toHaveBeenCalled(); }); });7. 企业级实现建议对于大型项目建议考虑以下扩展错误码体系建立统一的错误码规范便于前后端协作多语言支持根据请求头自动返回对应语言的错误消息错误分级区分操作错误可预期和程序错误需要立即修复错误聚合对高频相似错误进行聚合避免报警风暴自动恢复对于某些可恢复错误实现自动重试机制一个更完整的企业级实现可能如下class ErrorHandler { constructor(options) { this.strategies options.strategies; this.logger options.logger; this.metrics options.metrics; } async handle(promise, context {}) { try { const result await promise; return { success: true, data: result }; } catch (error) { const strategy this.getStrategy(error); const response strategy(error, context); this.logger.error(error, { context }); this.metrics.increment(error.${error.code}); if (strategy.shouldRetry) { await this.retry(promise, strategy.retryOptions); } return { success: false, error: response }; } } getStrategy(error) { return this.strategies[error.constructor.name] || this.strategies.default; } async retry(promise, options {}) { // 实现指数退避重试逻辑 } } // 使用示例 const handler new ErrorHandler({ /* 配置 */ }); const { success, data, error } await handler.handle(fetchData(), { requestId });