您现在的位置是:网站首页 > 错误处理策略文章详情
错误处理策略
陈川
【
Node.js
】
3468人已围观
7661字
在Node.js开发中,错误处理是保证应用健壮性的关键环节。不同的场景需要针对性的策略,从同步/异步错误捕获到进程级管理,合理的错误处理能显著提升系统容错能力。
同步错误处理
同步代码中的错误通常通过try/catch
块捕获。Node.js的同步API(如fs.readFileSync
)会直接抛出异常:
const fs = require('fs');
function readConfigSync(path) {
try {
const data = fs.readFileSync(path, 'utf8');
return JSON.parse(data);
} catch (err) {
console.error('同步操作失败:', err.message);
// 恢复默认配置
return { port: 3000 };
}
}
典型错误类型包括:
SyntaxError
:JSON解析失败TypeError
:参数类型错误SystemError
:文件系统操作失败
异步错误处理
回调模式
传统回调函数遵循Error-first约定,第一个参数总是错误对象:
fs.readFile('/nonexistent', (err, data) => {
if (err) {
if (err.code === 'ENOENT') {
console.warn('文件不存在,使用默认值');
} else {
console.error('不可恢复错误:', err.stack);
process.exit(1);
}
return;
}
// 正常处理逻辑
});
Promise处理
现代Node.js推荐使用Promise链式调用:
import { promises as fs } from 'fs';
async function loadUserData(userId) {
return fs.readFile(`users/${userId}.json`, 'utf8')
.then(JSON.parse)
.catch(err => {
if (err.code === 'ENOENT') {
return { id: userId, status: 'new' };
}
throw err; // 重新抛出未处理错误
});
}
异步函数错误处理
async/await语法需要显式try-catch:
async function processOrder(orderId) {
try {
const order = await db.getOrder(orderId);
const receipt = await generateReceipt(order);
await email.send(order.userEmail, receipt);
} catch (err) {
if (err instanceof DatabaseError) {
await logDatabaseFailure(err);
} else if (err instanceof EmailError) {
await queueRetry(orderId);
} else {
// 未预期错误
await criticalErrorHandler(err);
throw err;
}
}
}
事件发射器错误处理
EventEmitter需要监听error事件:
const { EventEmitter } = require('events');
class MyEmitter extends EventEmitter {}
const emitter = new MyEmitter();
emitter.on('error', (err) => {
console.error('发射器错误:', err.message);
// 防止进程崩溃
});
// 未处理error事件会导致进程退出
emitter.emit('error', new Error('测试错误'));
进程级错误处理
未捕获异常
process.on('uncaughtException', (err) => {
console.error('致命错误:', err);
// 记录状态后优雅退出
emergencyLogger.fatal(err).finally(() => {
process.exit(1);
});
});
未处理的Promise拒绝
process.on('unhandledRejection', (reason, promise) => {
console.warn('未处理的拒绝:', reason);
// 可以转换为未捕获异常
throw reason;
});
错误分类策略
可恢复错误
class RetryableError extends Error {
constructor(message, { maxRetries = 3 } = {}) {
super(message);
this.retryable = true;
this.maxRetries = maxRetries;
}
}
async function fetchWithRetry(url) {
let lastError;
for (let i = 0; i < 3; i++) {
try {
return await fetch(url);
} catch (err) {
lastError = err;
if (!err.retryable) break;
await sleep(1000 * (i + 1));
}
}
throw lastError;
}
业务逻辑错误
class ValidationError extends Error {
constructor(field, message) {
super(`字段验证失败: ${field}`);
this.field = field;
this.userMessage = message;
this.statusCode = 400;
}
}
router.post('/users', (req, res) => {
try {
validateUserInput(req.body);
// ...
} catch (err) {
if (err instanceof ValidationError) {
return res.status(err.statusCode).json({ error: err.userMessage });
}
next(err);
}
});
错误日志记录
结构化日志示例:
const { createLogger, transports, format } = require('winston');
const errorLogger = createLogger({
level: 'error',
format: format.combine(
format.timestamp(),
format.errors({ stack: true }),
format.json()
),
transports: [
new transports.File({ filename: 'error.log' }),
new transports.Console({
format: format.prettyPrint()
})
]
});
// 使用示例
try {
riskyOperation();
} catch (err) {
errorLogger.error('操作失败', {
error: err.message,
stack: err.stack,
context: { userId: 123 }
});
}
HTTP错误处理中间件
Express中间件示例:
function errorMiddleware(err, req, res, next) {
// 识别错误类型
const status = err.statusCode || 500;
const response = {
message: status >= 500 ? '服务器错误' : err.message
};
// 开发环境显示堆栈
if (process.env.NODE_ENV === 'development') {
response.stack = err.stack;
}
// 特殊错误类型处理
if (err instanceof MongooseError) {
response.details = err.errors;
}
res.status(status).json(response);
// 触发监控系统
if (status >= 500) {
monitoring.notify(err);
}
}
app.use(errorMiddleware);
资源清理策略
let resource;
try {
resource = acquireResource();
await useResource(resource);
} finally {
if (resource) {
await resource.release().catch(releaseErr => {
console.error('资源释放失败:', releaseErr);
});
}
}
错误转换模式
将底层错误转换为业务错误:
async function getUserProfile(userId) {
try {
return await db.query('SELECT * FROM users WHERE id = ?', [userId]);
} catch (err) {
if (err.code === 'ER_NO_SUCH_TABLE') {
throw new ApplicationError('系统维护中', { code: 'SERVICE_UNAVAILABLE' });
}
if (err.code === 'ER_BAD_FIELD_ERROR') {
throw new ApplicationError('数据库结构异常', { code: 'INTERNAL_ERROR' });
}
throw err;
}
}
防御性编程技巧
参数校验辅助函数:
function assert(condition, message, ErrorType = Error) {
if (!condition) {
throw new ErrorType(message);
}
}
function createUser(userData) {
assert(userData.email, '邮箱不能为空', ValidationError);
assert(/^.+@.+\..+$/.test(userData.email), '邮箱格式无效', ValidationError);
// ...
}
错误聚合报告
批量操作时的错误收集:
async function batchProcess(items) {
const results = [];
const errors = [];
await Promise.all(items.map(async (item) => {
try {
results.push(await processItem(item));
} catch (err) {
errors.push({
item,
error: err.message
});
}
}));
if (errors.length > 0) {
throw new BatchError('部分处理失败', { errors, results });
}
return results;
}
超时控制机制
async function withTimeout(promise, timeout, errorMessage) {
let timer;
const timeoutPromise = new Promise((_, reject) => {
timer = setTimeout(() => {
reject(new TimeoutError(errorMessage));
}, timeout);
});
try {
return await Promise.race([promise, timeoutPromise]);
} finally {
clearTimeout(timer);
}
}
// 使用示例
try {
const data = await withTimeout(
fetchAPI(),
5000,
'API响应超时'
);
} catch (err) {
if (err instanceof TimeoutError) {
// 启动备用方案
}
}
错误恢复模式
数据库连接重试示例:
class DatabaseConnection {
constructor() {
this.retryCount = 0;
this.maxRetries = 5;
}
async connect() {
while (this.retryCount < this.maxRetries) {
try {
return await this._attemptConnect();
} catch (err) {
this.retryCount++;
if (this.retryCount >= this.maxRetries) {
throw new Error(`连接失败,已重试${this.maxRetries}次`);
}
await this._waitForRetry();
}
}
}
async _attemptConnect() {
// 实际连接逻辑
}
async _waitForRetry() {
const delay = Math.min(1000 * 2 ** this.retryCount, 30000);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
错误边界设计
React组件错误边界在Node.js的类似实现:
function withErrorBoundary(component) {
return async function (...args) {
try {
return await component(...args);
} catch (err) {
await errorService.report(err);
return {
status: 'error',
message: '功能暂时不可用',
errorId: err.reportId
};
}
};
}
// 使用装饰器
const safeHandler = withErrorBoundary(async (req, res) => {
// 业务逻辑
});
上一篇: async/await语法糖
下一篇: 回调地狱问题与解决方案