async/await 中的异常处理核心机制是 try...catch,它能捕获 await 后的 promise 被拒绝时抛出的错误,就像处理同步异常一样;2. 当 await 一个被拒绝的 promise 时,javascript 运行时会将其表现为在当前行抛出同步错误,从而可以被 surrounding 的 try...catch 捕获;3. 常见模式包括细粒度捕获(在 async 函数内针对特定 await 操作捕获)和粗粒度捕获(在调用处捕获整个流程错误),以及“go 风格”返回 [error, result] 元组的方式;4. 常见陷阱包括错误被吞噬(catch 中未处理或重新抛出)、忘记使用 await 导致错误无法被捕获,以及 promise.all 的“快速失败”特性导致部分错误被忽略;5. 在大型应用中应通过全局错误监听(如 unhandledrejection)、自定义错误类、统一日志记录、错误监控平台集成和标准化 api 错误响应来实现统一的异常管理,确保错误不被遗漏并能及时响应,从而构建健壮的应用系统。

在
async/await
try...catch
async/await
try...catch
处理
async/await
try...catch
await
try...catch
async function fetchData() {
try {
// 假设这个函数可能会因为网络问题或服务器错误而抛出异常
const response = await fetch('https://api.example.com/data');
if (!response.ok) { // 检查HTTP状态码,非2xx通常视为错误
// 明确抛出错误,让上层捕获
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
console.log('数据获取成功:', data);
return data;
} catch (error) {
// 捕获fetch或json解析过程中可能发生的错误
console.error('数据获取失败:', error.message);
// 可以选择重新抛出错误,或者返回一个默认值/错误对象
throw error; // 向上层传播错误
} finally {
// 无论成功或失败,都会执行的清理工作
console.log('数据获取尝试结束。');
}
}
// 调用示例
(async () => {
try {
const result = await fetchData();
// 只有在fetchData成功时才执行
console.log('最终结果:', result);
} catch (e) {
// 捕获fetchData内部未处理或重新抛出的错误
console.error('顶层捕获到错误:', e.message);
}
})();这里的关键点是,你可以在
async
await
try...catch
async
try...catch
在我看来,
async/await
async
await
rejected
await
.catch()
try...catch
举个例子,假设你有一个 Promise 会失败:
function unreliablePromise() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('哎呀,出错了!'));
}, 100);
});
}
async function processData() {
console.log('开始处理数据...');
try {
const data = await unreliablePromise(); // 这里的Promise会拒绝
console.log('数据处理成功:', data); // 这行代码永远不会执行
} catch (error) {
console.error('捕获到错误:', error.message); // 错误在这里被捕获
}
console.log('处理结束。');
}
processData();
// 输出:
// 开始处理数据...
// 捕获到错误: 哎呀,出错了!
// 处理结束。可以看到,
unreliablePromise
await
catch
async
try...catch
async/await
在实际开发中,处理
async/await
常见的模式:
细粒度捕获与粗粒度捕获结合:
async
await
try...catch
async function getUserProfile(userId) {
try {
const user = await db.findUser(userId);
return user;
} catch (dbError) {
console.warn(`无法从数据库获取用户 ${userId} 的信息,尝试从缓存加载。`, dbError.message);
// 尝试从缓存获取
const cachedUser = await cache.get(userId);
if (cachedUser) return cachedUser;
throw new Error(`用户 ${userId} 不存在或无法访问。`); // 最终还是抛出错误
}
}async
try...catch
async function main() {
try {
const profile = await getUserProfile('someId');
console.log('用户资料:', profile);
} catch (e) {
console.error('处理用户资料时发生未知错误:', e.message);
// 给用户显示一个友好的错误提示
displayErrorMessage('加载用户资料失败,请稍后再试。');
}
}“Go 风格”错误处理(返回 [error, result]
// 辅助函数
function to<T>(promise: Promise<T>): Promise<[Error | null, T | undefined]> {
return promise.then(data => [null, data]).catch(err => [err, undefined]);
}
async function processMultipleRequests() {
const [err1, user] = await to(fetchUser());
if (err1) {
console.error('获取用户失败:', err1.message);
return; // 或者进行其他错误处理
}
const [err2, posts] = await to(fetchPosts(user.id));
if (err2) {
console.error('获取帖子失败:', err2.message);
return;
}
console.log('用户和帖子都获取成功:', user, posts);
}这种模式让错误处理变得非常显式,避免了深层嵌套的
try...catch
async/await
常见的陷阱:
“吞噬”错误(Swallowing Errors): 这是最危险的陷阱之一。在
catch
async function doSomethingRisky() {
try {
await someFailingOperation();
} catch (e) {
// ❌ 错误被吞噬了,外部调用者永远不知道这里出错了
// 什么都没做!
}
}最佳实践: 至少要记录日志,或者将错误重新抛出,或者返回一个明确的错误状态。
忘记 await
async
await
async
try...catch
await
async function callWithoutAwait() {
try {
failingAsyncFunction(); // ❌ 忘记了 await
console.log('这行代码可能会在错误发生前执行');
} catch (e) {
console.error('这个catch块捕获不到failingAsyncFunction的错误');
}
}
// failingAsyncFunction 的错误会成为一个未处理的 Promise 拒绝最佳实践: 始终记得
await
async
Promise.all
Promise.all
Promise.all
Promise.all
async function processAll() {
const p1 = Promise.resolve(1);
const p2 = Promise.reject(new Error('P2 failed'));
const p3 = Promise.resolve(3);
try {
const results = await Promise.all([p1, p2, p3]); // P2会使Promise.all立即拒绝
console.log(results);
} catch (e) {
console.error('Promise.all 捕获到错误:', e.message); // 只会捕获到 P2 的错误
}
}
processAll();最佳实践: 如果你需要所有 Promise 都执行完毕,无论成功与否,然后统一处理结果和错误,应该使用
Promise.allSettled
status: 'fulfilled'
status: 'rejected'
async function processAllSettled() {
const p1 = Promise.resolve(1);
const p2 = Promise.reject(new Error('P2 failed'));
const p3 = Promise.resolve(3);
const results = await Promise.allSettled([p1, p2, p3]);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index + 1} 成功:`, result.value);
} else {
console.error(`Promise ${index + 1} 失败:`, result.reason.message);
}
});
}
processAllSettled();这些模式和陷阱,说实话,即便经验丰富,也难免会犯错,关键在于如何快速发现并纠正。
在大型应用中,仅仅依靠散布在各处的
try...catch
全局错误捕获机制:
Node.js 环境: 使用
process.on('unhandledRejection')process.on('uncaughtException')unhandledRejection
catch
async/await
try...catch
uncaughtException
process.on('unhandledRejection', (reason, promise) => {
console.error('未处理的 Promise 拒绝:', reason);
// 记录日志,发送警报
// ⚠️ 在生产环境中,通常会选择优雅地关闭进程
// process.exit(1);
});
process.on('uncaughtException', (err) => {
console.error('未捕获的同步异常:', err);
// 记录日志,发送警报
// ⚠️ 同样,生产环境中应考虑优雅关闭
// process.exit(1);
});浏览器环境: 使用
window.addEventListener('unhandledrejection')window.addEventListener('error')window.addEventListener('unhandledrejection', (event) => {
console.error('未处理的 Promise 拒绝:', event.reason);
// 上报错误到监控系统
});
window.addEventListener('error', (event) => {
console.error('未捕获的同步错误:', event.error);
// 上报错误到监控系统
});这些全局监听器是最后的防线,它们能帮助你发现那些你代码中遗漏的
try...catch
自定义错误类: 创建自己的错误类,让错误信息更具语义化和可识别性。这比仅仅抛出
Error
class DatabaseError extends Error {
constructor(message: string, public code: number = 500) {
super(message);
this.name = 'DatabaseError';
}
}
class AuthenticationError extends Error {
constructor(message: string, public code: number = 401) {
super(message);
this.name = 'AuthenticationError';
}
}
async function login(username, password) {
const user = await db.findUser(username);
if (!user) {
throw new AuthenticationError('用户不存在。');
}
if (user.password !== password) {
throw new AuthenticationError('密码错误。', 403);
}
return user;
}
// 在上层捕获时,可以根据错误类型进行更精细的处理
try {
await login('test', '123');
} catch (e) {
if (e instanceof AuthenticationError) {
console.error(`认证失败: ${e.message} (Code: ${e.code})`);
} else if (e instanceof DatabaseError) {
console.error(`数据库操作失败: ${e.message}`);
} else {
console.error('未知错误:', e.message);
}
}自定义错误类让错误处理逻辑更清晰,也方便前端根据错误类型展示不同的提示。
统一的日志记录: 无论错误在哪里被捕获,都应该通过一个统一的日志系统(如 Winston, Pino, Log4js 等)进行记录。日志应该包含足够的信息,如错误栈、发生时间、请求ID(如果有的话)、用户ID等,以便于调试和追溯。
import logger from './utils/logger'; // 假设你有一个日志工具
async function processRequest(req, res) {
try {
// ... 业务逻辑
} catch (e) {
logger.error(`处理请求 ${req.path} 时发生错误:`, e);
res.status(500).send('服务器内部错误');
}
}错误监控与告警: 将错误日志集成到错误监控平台(如 Sentry, New Relic, Bugsnag, Datadog 等)。这些工具能聚合错误、去重、提供详细的错误上下文,并在错误率过高或出现严重错误时自动发送告警,帮助团队及时发现并解决问题。
API 错误响应标准化: 对于 Web API,确保所有错误响应都遵循统一的格式,例如包含
code
message
details
这些实践能帮助你构建一个健壮的、易于维护的、对错误有高可见度的应用程序。错误处理不是一蹴而就的,它是一个持续优化和完善的过程。
以上就是async/await中的异常如何处理?最佳实践是什么?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号