Async/Await 和 reject:为什么这对新手来说是个痛点?
你是否曾经编写过一个 async 函数,等待服务器响应,然后捕获到 UnhandledPromiseRejection?熟悉吗?这是开发者在转向 TypeScript 中的 async/await 时遇到的最常见问题之一。
在 Stack Overflow 上,问题 "How to reject in async/await syntax?" 获得了数千次浏览。这并非没有原因。乍一看,似乎只需返回 Promise.reject() 就能解决问题。但在实际项目中,情况要复杂得多:类型、调用链、错误上下文丢失等。
在本指南中,我们将详细讲解如何在 TypeScript 的 async 函数中正确拒绝 Promise,学习区分 throw 和 return Promise.reject(),并查看来自实际项目的实战示例。
基础:reject 在 async/await 中如何工作
在深入探讨之前,让我们先回顾一下基础知识。TypeScript 中的任何 async 函数都会返回一个 Promise。如果函数内部发生了 throw 或返回了 Promise.reject(),则该 Promise 会进入 rejected 状态。
// async/await 中最简单的 reject 示例
async function fetchUser(id: number): Promise<User> {
if (id <= 0) {
throw new Error('无效的用户 ID');
}
// ...获取用户的逻辑
}这里,throw 会自动转换为 reject。但如果我们想返回一个已经拒绝的 Promise 呢?
throw vs return Promise.reject()
在 TypeScript 中,两种方法都有效,但有一些细微差别:
- throw — 更符合习惯用法。它会停止函数的执行,并将控制权传递给最近的
catch。 - return Promise.reject() — 显式返回一个拒绝的 Promise。当需要从另一个函数传递错误时很有用。
// throw 和 return Promise.reject() 的区别
async function processOrder(orderId: string): Promise<OrderStatus> {
// 选项 1:throw
if (!orderId) {
throw new Error('需要订单 ID');
}
// 选项 2:return Promise.reject
if (orderId.startsWith('test_')) {
return Promise.reject(new Error('不允许测试订单'));
}
return { status: 'processing' };
}重要提示: TypeScript 在使用 return Promise.reject() 时并不总能正确推断类型。如果函数期望返回 Promise<T>,而您返回了 Promise.reject(),可能会出现类型错误。在这种情况下,最好使用 throw。
在 async 函数中 reject 的典型错误
即使是经验丰富的 TypeScript 开发者有时也会犯一些经典错误。以下是前三大错误:
1. 忘记在调用代码中处理 reject
async function main() {
// 不好:没有 try/catch
const user = await fetchUser(-1);
console.log(user); // UnhandledPromiseRejection!
}
// 好的做法:
async function mainSafe() {
try {
const user = await fetchUser(-1);
console.log(user);
} catch (error) {
console.error('获取用户失败:', error);
}
}2. 错误的错误类型标注
在 TypeScript 中,catch 块默认捕获 unknown 类型。这是正确的,但有时开发者会尝试直接访问 error.message 而不进行检查。
try {
await riskyOperation();
} catch (error) {
// 错误:error 是 unknown 类型
console.log(error.message); // ❌ 类型 'unknown' 上不存在属性 'message'
// 正确做法:
if (error instanceof Error) {
console.log(error.message);
} else {
console.log