异步函数
概述
异步函数(Async Functions)是现代 JavaScript 和 TypeScript 中处理异步操作的重要方式。TypeScript 为异步函数提供了完整的类型支持,包括 Promise 类型、async/await 语法的类型推断,以及异步函数的返回类型处理。通过 TypeScript 的类型系统,我们可以在编译时确保异步操作的类型安全,避免常见的异步编程错误。
Promise 类型
在了解异步函数之前,我们需要先理解 Promise 类型。Promise 是 TypeScript 中表示异步操作的类型,它接受一个类型参数,表示 Promise 解析后的值类型。
基本 Promise 类型
// Promise<string> 表示一个会解析为 string 的 Promise
const fetchData: Promise<string> = new Promise((resolve) => {
setTimeout(() => {
resolve("Hello, World!");
}, 1000);
});
// Promise<number> 表示一个会解析为 number 的 Promise
const calculateSum: Promise<number> = new Promise((resolve) => {
setTimeout(() => {
resolve(42);
}, 1000);
});
// Promise<void> 表示一个不返回值的 Promise
const logMessage: Promise<void> = new Promise((resolve) => {
setTimeout(() => {
console.log("Message logged");
resolve();
}, 1000);
});Promise 泛型类型
Promise<T> 是一个泛型类型,其中 T 是 Promise 解析后的值类型:
// 定义返回 Promise 的函数类型
type AsyncStringGetter = () => Promise<string>;
type AsyncNumberCalculator = (a: number, b: number) => Promise<number>;
// 使用示例
const getString: AsyncStringGetter = async () => {
return "Hello";
};
const add: AsyncNumberCalculator = async (a, b) => {
return a + b;
};Promise 的错误处理
Promise 可以表示成功或失败的情况,TypeScript 的类型系统会帮助我们处理这些情况:
// Promise 可能成功或失败
async function fetchUser(id: number): Promise<User> {
// 模拟 API 调用
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error("Failed to fetch user");
}
return response.json();
}
// 使用 try-catch 处理错误
async function getUser(id: number) {
try {
const user = await fetchUser(id);
// user 的类型是 User
console.log(user.name);
} catch (error) {
// error 的类型是 unknown(在严格模式下)
console.error("Error:", error);
}
}async/await 语法
async/await 是处理 Promise 的语法糖,让异步代码看起来像同步代码。TypeScript 完全支持 async/await 语法,并提供了完整的类型推断。
基本 async 函数
使用 async 关键字定义的函数会自动返回 Promise:
// async 函数自动返回 Promise
async function fetchData(): Promise<string> {
return "Hello, World!";
// 等价于:return Promise.resolve("Hello, World!");
}
// TypeScript 可以推断返回类型
async function getNumber() {
return 42; // 推断为 Promise<number>
}
// 显式指定返回类型
async function getUser(id: number): Promise<User> {
// 返回 User 对象
return { id, name: "Alice", email: "alice@example.com" };
}await 关键字
await 关键字用于等待 Promise 解析,只能在 async 函数中使用:
// 使用 await 等待 Promise 解析
async function processData() {
const data = await fetchData(); // data 的类型是 string
console.log(data.toUpperCase()); // 类型安全,可以调用 string 的方法
const number = await getNumber(); // number 的类型是 number
console.log(number * 2); // 类型安全,可以进行数学运算
}
// await 会"解包" Promise,获取内部的值类型
async function example() {
const promise: Promise<string> = fetchData();
const value: string = await promise; // await 将 Promise<string> 转换为 string
}异步函数的返回类型推断
TypeScript 可以自动推断异步函数的返回类型:
// TypeScript 自动推断返回类型为 Promise<string>
async function greet(name: string) {
return `Hello, ${name}!`;
}
// TypeScript 自动推断返回类型为 Promise<number>
async function calculate(a: number, b: number) {
return a + b;
}
// TypeScript 自动推断返回类型为 Promise<User>
async function createUser(name: string, email: string) {
return {
id: Date.now(),
name,
email
};
}提示
虽然 TypeScript 可以推断异步函数的返回类型,但显式声明返回类型可以让代码更清晰,也便于发现类型错误。
使用示例
示例 1:基础异步函数
// 定义用户类型
interface User {
id: number;
name: string;
email: string;
}
// 模拟 API 调用
async function fetchUser(id: number): Promise<User> {
// 模拟网络延迟
await new Promise(resolve => setTimeout(resolve, 1000));
// 返回用户数据
return {
id,
name: "Alice",
email: "alice@example.com"
};
}
// 使用异步函数
async function displayUser(id: number) {
const user = await fetchUser(id);
// user 的类型是 User,可以安全访问属性
console.log(`User: ${user.name} (${user.email})`);
}
// 调用异步函数
displayUser(1);示例 2:错误处理
// 定义错误类型
class ApiError extends Error {
constructor(
public statusCode: number,
public message: string
) {
super(message);
this.name = "ApiError";
}
}
// 可能抛出错误的异步函数
async function fetchUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new ApiError(response.status, "Failed to fetch user");
}
return response.json();
}
// 使用 try-catch 处理错误
async function getUser(id: number) {
try {
const user = await fetchUser(id);
// user 的类型是 User
return user;
} catch (error) {
// error 的类型是 unknown(在严格模式下)
if (error instanceof ApiError) {
console.error(`API Error ${error.statusCode}: ${error.message}`);
} else {
console.error("Unknown error:", error);
}
throw error; // 重新抛出错误
}
}示例 3:并行异步操作
// 并行执行多个异步操作
async function fetchUserData(userId: number) {
// 使用 Promise.all 并行执行
const [user, posts, comments] = await Promise.all([
fetchUser(userId), // Promise<User>
fetchUserPosts(userId), // Promise<Post[]>
fetchUserComments(userId) // Promise<Comment[]>
]);
// TypeScript 正确推断类型
// user: User
// posts: Post[]
// comments: Comment[]
return {
user,
posts,
comments
};
}
// 辅助函数
async function fetchUserPosts(userId: number): Promise<Post[]> {
// 模拟 API 调用
return [];
}
async function fetchUserComments(userId: number): Promise<Comment[]> {
// 模拟 API 调用
return [];
}示例 4:泛型异步函数
// 泛型异步函数,可以处理任意类型
async function fetchData<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
// 使用泛型异步函数
interface User {
id: number;
name: string;
}
interface Product {
id: number;
title: string;
price: number;
}
// 获取用户数据
async function getUser(id: number): Promise<User> {
return fetchData<User>(`/api/users/${id}`);
}
// 获取产品数据
async function getProduct(id: number): Promise<Product> {
return fetchData<Product>(`/api/products/${id}`);
}
// 使用示例
async function example() {
const user = await getUser(1); // 类型:User
const product = await getProduct(1); // 类型:Product
console.log(user.name); // 类型安全
console.log(product.price); // 类型安全
}示例 5:异步函数链式调用
// 定义数据处理函数
async function validateUser(data: unknown): Promise<User> {
// 验证逻辑
if (typeof data === 'object' && data !== null) {
const user = data as User;
if (user.id && user.name && user.email) {
return user;
}
}
throw new Error("Invalid user data");
}
async function saveUser(user: User): Promise<User> {
// 保存逻辑
return user;
}
async function sendWelcomeEmail(user: User): Promise<void> {
// 发送邮件逻辑
console.log(`Sending welcome email to ${user.email}`);
}
// 链式调用异步函数
async function createUser(data: unknown): Promise<User> {
// 步骤 1:验证数据
const validatedUser = await validateUser(data);
// 步骤 2:保存用户
const savedUser = await saveUser(validatedUser);
// 步骤 3:发送欢迎邮件
await sendWelcomeEmail(savedUser);
// 返回保存的用户
return savedUser;
}示例 6:异步函数作为参数
// 定义异步回调函数类型
type AsyncCallback<T> = (data: T) => Promise<void>;
type AsyncTransformer<T, R> = (data: T) => Promise<R>;
// 处理数据的异步函数
async function processData<T>(
data: T,
transformer: AsyncTransformer<T, T>,
callback: AsyncCallback<T>
): Promise<T> {
// 转换数据
const transformed = await transformer(data);
// 执行回调
await callback(transformed);
// 返回转换后的数据
return transformed;
}
// 使用示例
async function example() {
const user: User = {
id: 1,
name: "Alice",
email: "alice@example.com"
};
// 定义转换函数
const transformer: AsyncTransformer<User, User> = async (user) => {
// 模拟数据处理
return { ...user, name: user.name.toUpperCase() };
};
// 定义回调函数
const callback: AsyncCallback<User> = async (user) => {
console.log(`Processing user: ${user.name}`);
};
// 处理数据
const result = await processData(user, transformer, callback);
// result 的类型是 User
}示例 7:条件异步操作
// 根据条件执行不同的异步操作
async function fetchData(
id: number,
useCache: boolean = false
): Promise<User> {
if (useCache) {
// 从缓存获取
const cached = await getFromCache(id);
if (cached) {
return cached;
}
}
// 从 API 获取
const user = await fetchFromApi(id);
// 保存到缓存
if (useCache) {
await saveToCache(id, user);
}
return user;
}
// 辅助函数
async function getFromCache(id: number): Promise<User | null> {
// 模拟缓存获取
return null;
}
async function fetchFromApi(id: number): Promise<User> {
// 模拟 API 调用
return {
id,
name: "Alice",
email: "alice@example.com"
};
}
async function saveToCache(id: number, user: User): Promise<void> {
// 模拟缓存保存
console.log(`Caching user ${id}`);
}Promise 工具类型
TypeScript 提供了一些工具类型来处理 Promise:
Awaited 类型
Awaited<T> 类型(TypeScript 4.5+)可以获取 Promise 解析后的类型:
// Awaited 类型可以"解包" Promise
type StringPromise = Promise<string>;
type UnwrappedString = Awaited<StringPromise>; // string
type NestedPromise = Promise<Promise<number>>;
type UnwrappedNumber = Awaited<NestedPromise>; // number
// 实际应用:获取异步函数的返回类型
async function fetchUser(id: number): Promise<User> {
return { id, name: "Alice", email: "alice@example.com" };
}
type UserType = Awaited<ReturnType<typeof fetchUser>>; // User手动解包 Promise 类型
在 TypeScript 4.5 之前,可以使用条件类型手动解包 Promise:
// 手动解包 Promise 类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type StringPromise = Promise<string>;
type Unwrapped = UnwrapPromise<StringPromise>; // string
// 处理嵌套 Promise
type UnwrapPromiseDeep<T> = T extends Promise<infer U>
? U extends Promise<any>
? UnwrapPromiseDeep<U>
: U
: T;
type Nested = Promise<Promise<number>>;
type UnwrappedDeep = UnwrapPromiseDeep<Nested>; // number类型检查示例
常见错误
// ❌ 错误:忘记使用 await
async function example() {
const user = fetchUser(1); // 类型是 Promise<User>,不是 User
console.log(user.name); // Error: Property 'name' does not exist on type 'Promise<User>'
}
// ❌ 错误:在非 async 函数中使用 await
function badExample() {
const user = await fetchUser(1); // Error: 'await' expressions are only allowed within async functions
}
// ❌ 错误:返回类型不匹配
async function badReturn(): Promise<string> {
return 42; // Error: Type 'number' is not assignable to type 'Promise<string>'
}
// ❌ 错误:Promise 类型参数错误
async function badPromise(): Promise<User> {
return Promise.resolve("hello"); // Error: Type 'Promise<string>' is not assignable to type 'Promise<User>'
}正确写法
// ✅ 正确:使用 await 等待 Promise
async function example() {
const user = await fetchUser(1); // 类型是 User
console.log(user.name); // 类型安全
}
// ✅ 正确:在 async 函数中使用 await
async function goodExample() {
const user = await fetchUser(1);
return user;
}
// ✅ 正确:返回类型匹配
async function goodReturn(): Promise<string> {
return "hello"; // 自动包装为 Promise<string>
}
// ✅ 正确:Promise 类型参数正确
async function goodPromise(): Promise<User> {
return { id: 1, name: "Alice", email: "alice@example.com" };
}注意事项
提示
async函数总是返回Promise,即使函数体内没有使用await- TypeScript 可以自动推断异步函数的返回类型,但显式声明可以让代码更清晰
- 使用
await时,TypeScript 会自动"解包" Promise,获取内部的值类型 Promise.all可以并行执行多个异步操作,提高性能- 使用泛型可以让异步函数更加灵活和可复用
注意
await只能在async函数中使用,否则会出现编译错误- 异步函数的错误处理需要使用
try-catch,TypeScript 不会自动处理 Promise 的拒绝 - 忘记使用
await会导致返回Promise而不是实际值,这是常见的错误 - Promise 的类型参数表示成功时的值类型,错误类型不在类型系统中(使用
unknown或Error) - 异步函数的返回类型是
Promise<T>,不是T
重要
- 异步函数是 TypeScript 类型系统的重要组成部分,理解异步类型对于编写类型安全的异步代码非常重要
Promise类型是泛型类型,必须指定类型参数才能获得完整的类型检查- 错误处理是异步编程的重要部分,应该始终考虑错误情况
- 使用
Awaited<T>类型可以方便地获取 Promise 解析后的类型(TypeScript 4.5+)
信息
异步函数与同步函数的区别:
- 同步函数:直接返回值,类型是
T - 异步函数:返回 Promise,类型是
Promise<T> - await:将
Promise<T>转换为T
异步函数的优势:
- 类型安全:编译时检查异步操作的类型
- 代码清晰:
async/await让异步代码看起来像同步代码 - 错误处理:可以使用
try-catch处理异步错误 - IDE 支持:完整的类型提示和自动补全
实际应用场景:
- API 调用和数据获取
- 文件读写操作
- 数据库查询
- 定时任务和延迟操作
- 事件处理和回调
相关链接
- 函数类型 - 了解函数类型的基础知识
- 泛型 - 学习泛型的使用,可以用于异步函数
- 类型推断 - 了解 TypeScript 的类型推断机制
- 工具类型 - 学习 Awaited 等工具类型的使用
- TypeScript 官方文档 - async 函数