文章目录

TypeScript 异步编程:Promise 与 async/await

发布于 2026-04-02 22:16:16 · 浏览 9 次 · 评论 0 条

TypeScript 异步编程:Promise 与 async/await

TypeScript 中处理异步操作的核心工具是 Promiseasync/await。它们帮助你避免“回调地狱”,让代码更清晰、可读性更强,同时保留类型安全。


理解 Promise 的基本结构

Promise 是一个表示异步操作最终完成或失败的对象。它有三种状态:

  • pending(进行中):初始状态。
  • fulfilled(已成功):操作成功完成。
  • rejected(已失败):操作失败。

创建一个返回 Promise 的函数:

function fetchData(url: string): Promise<string> {
  return new Promise((resolve, reject) => {
    // 模拟网络请求
    setTimeout(() => {
      if (url.includes('error')) {
        reject(new Error('请求失败'));
      } else {
        resolve(`数据来自 ${url}`);
      }
    }, 1000);
  });
}
```

**调用**该函数并处理结果:

```typescript
fetchData('https://api.example.com/data')
  .then(data => console.log(data))
  .catch(err => console.error(err.message));
```

- `.then()` 处理成功结果。
- `.catch()` 捕获错误。

---

## 使用 async/await 简化异步逻辑

`async/await` 是 `Promise` 的语法糖,让异步代码看起来像同步代码。

**定义**一个 `async` 函数:

```typescript
async function loadUserData(userId: number): Promise<string> {
  const url = `https://api.example.com/users/${userId}`;
  const data = await fetchData(url);
  return data;
}
  • 函数前加 async,表示它会返回一个 Promise
  • 在函数内部使用 await 等待 Promise 完成。

调用 async 函数:

loadUserData(123)
  .then(user => console.log('用户信息:', user))
  .catch(err => console.error('加载失败:', err.message));

或者在另一个 async 函数中直接 await

async function displayUser() {
  try {
    const user = await loadUserData(123);
    console.log('用户信息:', user);
  } catch (err) {
    console.error('加载失败:', (err as Error).message);
  }
}
  • 务必用 try...catch 包裹 await 调用,否则错误会变成未处理的 Promise rejection。

并发执行多个异步任务

当需要同时发起多个独立请求时,不要用 await 逐个等待,这会串行执行、浪费时间。

使用 Promise.all 并行执行:

async function fetchMultipleUrls(urls: string[]): Promise<string[]> {
  const promises = urls.map(url => fetchData(url));
  return await Promise.all(promises);
}
  • Promise.all 接收一个 Promise 数组,返回一个新 Promise
  • 所有子 Promise 都成功时,返回结果数组;任意一个失败,整个操作立即失败。

如果希望部分失败不影响其他任务,使用 Promise.allSettled

async function fetchWithFallback(urls: string[]): Promise<string[]> {
  const results = await Promise.allSettled(
    urls.map(url => fetchData(url))
  );
  return results
    .filter(result => result.status === 'fulfilled')
    .map(result => (result as PromiseFulfilledResult<string>).value);
}
  • Promise.allSettled 总是等到所有 Promise 结束,无论成功或失败。
  • 返回数组包含每个任务的状态和值。

错误处理的最佳实践

异步错误必须显式捕获,否则会导致程序静默失败。

避免在顶层 async 函数外遗漏 .catch()

// 危险:未处理的 rejection
displayUser();

正确做法:始终链式调用 .catch(),或在 async 函数内部使用 try...catch

对于复杂流程,可以封装统一的错误处理逻辑:

async function safeFetch<T>(
  fn: () => Promise<T>,
  fallback: T
): Promise<T> {
  try {
    return await fn();
  } catch (err) {
    console.warn('操作失败,使用默认值', err);
    return fallback;
  }
}

// 使用
const data = await safeFetch(
  () => fetchData('https://api.example.com/config'),
  '{"theme":"light"}'
);

类型推导与显式标注

TypeScript 能自动推导 Promise 的返回类型,但显式标注更安全。

推荐显式声明返回类型

// 好:明确知道返回 Promise<string>
async function getName(): Promise<string> {
  return 'Alice';
}

// 避免:TypeScript 推导为 Promise<unknown>
async function badExample() {
  return Math.random() > 0.5 ? 'success' : 42; // 类型不一致
}

如果异步函数可能返回不同类型的值,使用联合类型:

async function fetchEither(
  flag: boolean
): Promise<string | number> {
  return flag ? '文本' : 42;
}

常见陷阱与规避方法

  1. 忘记 await 导致拿到 Promise 对象而非值

    const result = fetchData('url'); // result 是 Promise<string>,不是 string
    console.log(result.length); // 错误!
  2. 在循环中顺序 await,造成性能瓶颈

    // 错误:串行执行
    for (const url of urls) {
      const data = await fetchData(url);
      process(data);
    }
    
    // 正确:并行执行
    const promises = urls.map(url => fetchData(url));
    const results = await Promise.all(promises);
    results.forEach(process);
  3. 未处理 Promise rejection
    启用 TypeScript 的 --strict 模式,并在运行时监听未处理的 rejection:

    process.on('unhandledRejection', reason => {
      console.error('未捕获的异步错误:', reason);
    });

实战示例:带重试机制的 HTTP 请求

结合 Promiseasync/await 实现一个带重试功能的请求函数:

async function fetchWithRetry(
  url: string,
  maxRetries: number = 3
): Promise<string> {
  let lastError: Error;

  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fetchData(url);
    } catch (err) {
      lastError = err as Error;
      console.log(`第 ${i + 1} 次尝试失败,${maxRetries - i - 1} 次重试机会`);
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); // 指数退避
    }
  }

  throw lastError!;
}

调用

try {
  const data = await fetchWithRetry('https://unstable-api.com/data');
  console.log('最终获取数据:', data);
} catch (err) {
  console.error('所有重试均失败:', (err as Error).message);
}

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文