4 种姿势
1. try / catch
try {
JSON.parse(badInput);
} catch (err) {
console.error('解析失败:', err.message);
}
2. try / catch / finally
let conn;
try {
conn = await db.connect();
await conn.query('SELECT 1');
} catch (err) {
console.error(err);
} finally {
if (conn) await conn.close(); // 永远跑
}
3. 抛错
throw new Error('Something broke');
throw new TypeError('Expected string');
throw new RangeError('Out of bounds');
4. Promise 错误(异步章节细讲)
asyncFn().catch(err => console.error(err));
Error 类家族
Node 内置:
| 类 | 含义 |
|---|---|
Error |
通用 |
TypeError |
类型不对 |
RangeError |
数字 / 索引超范围 |
SyntaxError |
语法错(很少手动抛) |
ReferenceError |
引用未定义 |
URIError |
URI 相关 |
try {
null.foo;
} catch (err) {
console.log(err instanceof TypeError); // true
console.log(err.name); // 'TypeError'
console.log(err.message); // "Cannot read properties of null (reading 'foo')"
console.log(err.stack); // 调用栈
}
自定义错误
class ValidationError extends Error {
constructor(field, message) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
try {
throw new ValidationError('email', 'Invalid format');
} catch (err) {
if (err instanceof ValidationError) {
console.log(`${err.field}: ${err.message}`);
} else {
throw err; // 不认识的错误重抛
}
}
async / await 里的错误
async function getUser(id) {
try {
const r = await fetch(`/users/${id}`);
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return r.json();
} catch (err) {
console.error('获取失败:', err);
throw err; // 传给调用方
}
}
⚠ async 函数里 throw 等同 Promise reject——上游必须 await 或 .catch 才会捕获。
错误聚合(Node 15+)
const results = await Promise.allSettled([fn1(), fn2(), fn3()]);
const errors = results.filter(r => r.status === 'rejected').map(r => r.reason);
if (errors.length) {
throw new AggregateError(errors, 'Some operations failed');
}
未捕获错误
Node 进程级捕获兜底:
process.on('uncaughtException', (err) => {
console.error('💥 Uncaught:', err);
// 通常应该 process.exit(1) 让 systemd / pm2 重启
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('💥 Unhandled Rejection:', reason);
process.exit(1);
});
⚠ 这些是最后防线——不要用它们当正常流程错误处理。
错误传播规范
// ✓ 让错误冒到能处理的地方
async function readConfig() {
return JSON.parse(await fs.readFile('config.json', 'utf8'));
// 让调用方决定怎么处理
}
// ❌ 吞掉错误
async function readConfigBad() {
try {
return JSON.parse(await fs.readFile('config.json', 'utf8'));
} catch (err) {
return {}; // 静默失败 → bug 难找
}
}
// ✓ 转换并重抛
async function readConfigOk() {
try {
return JSON.parse(await fs.readFile('config.json', 'utf8'));
} catch (err) {
throw new Error(`Config load failed: ${err.message}`);
}
}
实用工具:error.cause
try {
await db.query(...);
} catch (err) {
throw new Error('Failed to fetch user', { cause: err });
// 保留原始错误的堆栈和上下文
}
打印时:
console.error(err);
// Error: Failed to fetch user
// at ...
// [cause]: Error: connection refused
// at ...
坑
- 别忘记重抛不认识的错误(
instanceof判断后)—— 不然吃掉错的根因 - async 函数没 await 就不会捕获——
fn()抛错变成 unhandled rejection try块里只 try 可能抛错的部分——别把整段函数包起来- 错误信息别泄露敏感数据(密码、token、内部路径)
下一篇起进入异步章节:callbacks → promises → async/await → event loop。