摘要:
本文系统解析 JavaScript 异常捕获的核心机制,涵盖同步代码、异步回调、Promise 链的完整错误处理方案。通过 20+ 实战场景,揭示未捕获异常的致命隐患,并对比不同处理模式的性能差异,为构建健壮应用奠定基础。
一、异常处理基础:同步代码捕获
try-catch-finally 标准结构:
javascript
function parseJSON(json) {
try {
console.log("开始解析");
const result = JSON.parse(json);
return result;
} catch (err) {
console.error("解析失败:", err.message);
return null;
} finally {
console.log("解析流程结束"); // 始终执行
}
}
parseJSON('{"name": "Alice"}'); // 成功
parseJSON('invalid_json'); // 捕获错误
性能关键点:
javascript
// 避免在 try 块中声明函数(V8 优化失效)
try {
function riskyOperation() { /* ... */ } // 反模式!
} catch (err) {}
错误类型识别:
javascript
try {
// 可能抛出多种错误
localStorage.setItem('key', data); // 可能 QuotaExceededError
fetch(url); // 可能 TypeError
} catch (err) {
if (err instanceof DOMException) {
handleStorageError(err);
} else if (err instanceof TypeError) {
handleNetworkError(err);
} else {
throw err; // 重新抛出未知错误
}
}
二、异步回调的异常困境
回调函数的错误吞噬:
javascript
// 无法捕获的异常
try {
setTimeout(() => {
throw new Error("异步炸弹");
}, 100);
} catch (err) {
console.log("永远不会执行");
}
// 控制台输出:
// Uncaught Error: 异步炸弹
Node.js 错误优先约定:
javascript
fs.readFile('config.json', (err, data) => {
if (err) {
return handleError(err); // 必须显式处理
}
processConfig(data);
});
解决方案:Domain 模块(已弃用):
javascript
// 历史方案(仅作了解)
const domain = require('domain');
const d = domain.create();
d.on('error', err => {
console.error("捕获异步错误:", err);
});
d.run(() => {
setTimeout(() => { throw new Error("被捕获") }, 100);
});
三、Promise 的异常处理艺术
基础错误捕获:
javascript
fetchData()
.then(data => process(data))
.catch(err => {
console.error("请求失败:", err);
return fallbackData;
});
高级模式:错误冒泡阻断:
javascript
const p = new Promise((resolve, reject) => {
resolve("初始值");
});
p.then(val => {
throw new Error("步骤1失败");
return val;
}).then(val => {
console.log("永远不会执行");
}).catch(err => {
console.log("捕获:", err.message); // "步骤1失败"
});
finally 的不可替代性:
javascript
let loading = true;
fetchData()
.then(data => render(data))
.catch(err => showError(err))
.finally(() => {
loading = false; // 无论成功失败都执行
});
四、Async/Await 的优雅处理
基础 try-catch 模式:
javascript
async function loadUser() {
try {
const user = await fetchUser();
const orders = await fetchOrders(user.id);
return { user, orders };
} catch (err) {
console.error("加载失败:", err);
return { user: null, orders: [] };
}
}
并行请求的错误处理:
javascript
async function loadDashboard() {
try {
const [user, products] = await Promise.all([
fetchUser().catch(err => ({ error: err })),
fetchProducts().catch(err => ({ error: err }))
]);
if (user.error || products.error) {
throw new AggregateError([user.error, products.error]);
}
return { user, products };
} catch (err) {
if (err instanceof AggregateError) {
err.errors.forEach(e => logError(e));
}
return null;
}
}
拒绝捕获的陷阱:
javascript
// 危险:未处理的拒绝
async function riskyOperation() {
const promise = fetchData();
// ...其他操作
await promise; // 若此前请求已失败,此处抛出未捕获异常
}
// 安全方案
async function safeOperation() {
const promise = fetchData().catch(err => err); // 预捕获
// ...
const result = await promise;
if (result instanceof Error) {
handleError(result);
}
}
五、全局异常捕获机制
浏览器环境:
javascript
// 同步错误
window.onerror = (msg, url, line, col, err) => {
console.error(`全局捕获: ${err.stack}`);
return true; // 阻止默认控制台输出
};
// 异步错误
window.addEventListener('error', event => {
console.error("全局异步错误:", event.error);
});
// Promise 拒绝
window.addEventListener('unhandledrejection', event => {
console.error("未处理的拒绝:", event.reason);
event.preventDefault(); // 阻止控制台报错
});
Node.js 环境:
javascript
process.on('uncaughtException', err => {
console.error("未捕获异常:", err);
// 必须退出进程!
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
console.error("未处理的拒绝:", reason);
});
框架中的最佳实践:
javascript
// Express 错误处理中间件
app.use((err, req, res, next) => {
if (err instanceof ValidationError) {
return res.status(400).json({ error: err.message });
}
logErrorToService(err);
res.status(500).send('服务器错误');
});
// Koa 错误处理
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
ctx.body = { error: err.message };
ctx.app.emit('error', err, ctx); // 触发全局事件
}
});
结语
本篇深入探讨了 JavaScript 异常处理的基础与异步场景解决方案,核心要点:
- 同步代码使用 try-catch 结构化处理
- 异步回调需遵循错误优先约定
- Promise 链式捕获与 async/await 结合
- 全局捕获作为最后防线
本文是构建健壮应用的基石,如果对你有帮助,请点赞收藏支持!关注作者获取更多工程化实践指南。