Skip to content

异步函数

概述

异步函数(Async Functions)是现代 JavaScript 和 TypeScript 中处理异步操作的重要方式。TypeScript 为异步函数提供了完整的类型支持,包括 Promise 类型、async/await 语法的类型推断,以及异步函数的返回类型处理。通过 TypeScript 的类型系统,我们可以在编译时确保异步操作的类型安全,避免常见的异步编程错误。

Promise 类型

在了解异步函数之前,我们需要先理解 Promise 类型。Promise 是 TypeScript 中表示异步操作的类型,它接受一个类型参数,表示 Promise 解析后的值类型。

基本 Promise 类型

typescript
// 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 解析后的值类型:

typescript
// 定义返回 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 的类型系统会帮助我们处理这些情况:

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

typescript
// 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 函数中使用:

typescript
// 使用 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
// 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:基础异步函数

typescript
// 定义用户类型
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:错误处理

typescript
// 定义错误类型
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:并行异步操作

typescript
// 并行执行多个异步操作
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:泛型异步函数

typescript
// 泛型异步函数,可以处理任意类型
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:异步函数链式调用

typescript
// 定义数据处理函数
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:异步函数作为参数

typescript
// 定义异步回调函数类型
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:条件异步操作

typescript
// 根据条件执行不同的异步操作
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 解析后的类型:

typescript
// 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:

typescript
// 手动解包 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

类型检查示例

常见错误

typescript
// ❌ 错误:忘记使用 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>'
}

正确写法

typescript
// ✅ 正确:使用 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 的类型参数表示成功时的值类型,错误类型不在类型系统中(使用 unknownError
  • 异步函数的返回类型是 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 调用和数据获取
  • 文件读写操作
  • 数据库查询
  • 定时任务和延迟操作
  • 事件处理和回调

相关链接

基于 VitePress 构建