TypeScript异步编程模式Skill typescript-async-patterns

这个技能涵盖了TypeScript中的异步编程模式,包括Promises、async/await、异步迭代器、错误处理和高级模式,用于构建稳健的异步应用程序。它帮助开发者高效处理异步操作,特别适用于API调用、I/O操作和实时数据处理。关键词:TypeScript, 异步编程, Promises, async/await, 错误处理, 异步迭代器, 重试模式, 超时控制, 取消机制, API开发。

前端开发 0 次安装 2 次浏览 更新于 3/25/2026

name: typescript-async-patterns user-invocable: false description: 使用TypeScript异步模式,包括Promises、async/await和带类型检查的异步迭代器。在编写异步TypeScript代码时使用。 allowed-tools:

  • Bash
  • Read
  • Write
  • Edit

TypeScript异步模式

掌握TypeScript中的异步编程模式,包括Promises、async/await、错误处理、异步迭代器以及构建稳健异步应用程序的高级模式。

Promises和async/await

基本Promise创建

// 创建一个Promise
function delay(ms: number): Promise<void> {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

// 带值的Promise
function fetchUserData(userId: string): Promise<User> {
  return new Promise((resolve, reject) => {
    // 模拟API调用
    setTimeout(() => {
      if (userId) {
        resolve({ id: userId, name: 'John Doe' });
      } else {
        reject(new Error('无效的用户ID'));
      }
    }, 1000);
  });
}

// 使用Promise
fetchUserData('123')
  .then((user) => {
    console.log(user.name);
  })
  .catch((error) => {
    console.error('错误:', error.message);
  });

async/await语法

interface User {
  id: string;
  name: string;
  email: string;
}

interface Post {
  id: string;
  userId: string;
  title: string;
  content: string;
}

// 异步函数声明
async function getUserPosts(userId: string): Promise<Post[]> {
  try {
    const user = await fetchUserData(userId);
    const posts = await fetchPostsByUser(user.id);
    return posts;
  } catch (error) {
    console.error('获取用户帖子失败:', error);
    throw error;
  }
}

// 异步箭头函数
const getUserProfile = async (userId: string): Promise<User> => {
  const user = await fetchUserData(userId);
  return user;
};

// 使用async/await
async function main() {
  const posts = await getUserPosts('123');
  console.log(`找到 ${posts.length} 个帖子`);
}

类型安全的Promise包装器

type Result<T, E = Error> =
  | { success: true; data: T }
  | { success: false; error: E };

async function safeAsync<T>(
  promise: Promise<T>
): Promise<Result<T>> {
  try {
    const data = await promise;
    return { success: true, data };
  } catch (error) {
    return {
      success: false,
      error: error instanceof Error ? error : new Error(String(error)),
    };
  }
}

// 用法
async function example() {
  const result = await safeAsync(fetchUserData('123'));

  if (result.success) {
    console.log(result.data.name);
  } else {
    console.error(result.error.message);
  }
}

Promise链式调用和组合

链式Promises

interface ApiResponse<T> {
  data: T;
  status: number;
}

function fetchData<T>(url: string): Promise<ApiResponse<T>> {
  return fetch(url)
    .then((response) => {
      if (!response.ok) {
        throw new Error(`HTTP错误!状态码:${response.status}`);
      }
      return response.json();
    })
    .then((data) => ({
      data,
      status: 200,
    }));
}

// 链式多个异步操作
function processUserData(userId: string): Promise<string> {
  return fetchUserData(userId)
    .then((user) => fetchPostsByUser(user.id))
    .then((posts) => posts.filter((post) => post.title.includes('TypeScript')))
    .then((filteredPosts) => `找到 ${filteredPosts.length} 个TypeScript帖子`)
    .catch((error) => {
      console.error('链式调用中的错误:', error);
      return '处理用户数据失败';
    });
}

组合异步函数

type AsyncFunction<T, R> = (input: T) => Promise<R>;

function pipe<T, A, B>(
  fn1: AsyncFunction<T, A>,
  fn2: AsyncFunction<A, B>
): AsyncFunction<T, B> {
  return async (input: T) => {
    const result1 = await fn1(input);
    return fn2(result1);
  };
}

// 用法
const getUserId = async (username: string): Promise<string> => {
  // 查找用户ID
  return '123';
};

const getUserData = async (userId: string): Promise<User> => {
  return fetchUserData(userId);
};

const getUserByUsername = pipe(getUserId, getUserData);

// 使用组合函数
const user = await getUserByUsername('johndoe');

异步代码中的错误处理

使用try-catch和async/await

class ApiError extends Error {
  constructor(
    message: string,
    public statusCode: number,
    public response?: unknown
  ) {
    super(message);
    this.name = 'ApiError';
  }
}

async function fetchWithErrorHandling<T>(url: string): Promise<T> {
  try {
    const response = await fetch(url);

    if (!response.ok) {
      throw new ApiError(
        `HTTP错误!状态码:${response.status}`,
        response.status
      );
    }

    const data = await response.json();
    return data;
  } catch (error) {
    if (error instanceof ApiError) {
      console.error(`API错误 ${error.statusCode}:${error.message}`);
    } else if (error instanceof TypeError) {
      console.error('网络错误:', error.message);
    } else {
      console.error('未知错误:', error);
    }
    throw error;
  }
}

错误恢复模式

async function fetchWithFallback<T>(
  primaryUrl: string,
  fallbackUrl: string
): Promise<T> {
  try {
    return await fetchWithErrorHandling<T>(primaryUrl);
  } catch (error) {
    console.warn('主获取失败,尝试备用');
    return await fetchWithErrorHandling<T>(fallbackUrl);
  }
}

// 多个备用
async function fetchWithMultipleFallbacks<T>(
  urls: string[]
): Promise<T> {
  let lastError: Error | undefined;

  for (const url of urls) {
    try {
      return await fetchWithErrorHandling<T>(url);
    } catch (error) {
      lastError = error instanceof Error ? error : new Error(String(error));
      console.warn(`从 ${url} 获取失败,尝试下一个...`);
    }
  }

  throw new Error(
    `所有获取都失败。最后错误:${lastError?.message ?? '未知'}`
  );
}

类型化的错误处理

class ValidationError extends Error {
  constructor(message: string, public field: string) {
    super(message);
    this.name = 'ValidationError';
  }
}

class NetworkError extends Error {
  constructor(message: string, public url: string) {
    super(message);
    this.name = 'NetworkError';
  }
}

type AppError = ValidationError | NetworkError | Error;

async function handleUserUpdate(userId: string, data: unknown): Promise<void> {
  try {
    await validateUserData(data);
    await updateUser(userId, data);
  } catch (error) {
    if (error instanceof ValidationError) {
      console.error(`字段 ${error.field} 的验证错误:${error.message}`);
    } else if (error instanceof NetworkError) {
      console.error(`${error.url} 的网络错误:${error.message}`);
    } else if (error instanceof Error) {
      console.error(`意外错误:${error.message}`);
    }
    throw error;
  }
}

Promise组合器

Promise.all

interface UserData {
  profile: User;
  posts: Post[];
  comments: Comment[];
}

async function fetchUserDataParallel(userId: string): Promise<UserData> {
  const [profile, posts, comments] = await Promise.all([
    fetchUserData(userId),
    fetchPostsByUser(userId),
    fetchCommentsByUser(userId),
  ]);

  return { profile, posts, comments };
}

// 类型安全的Promise.all,带元组
async function fetchMultipleResources() {
  const [users, posts, settings] = await Promise.all([
    fetchUsers(),      // Promise<User[]>
    fetchPosts(),      // Promise<Post[]>
    fetchSettings(),   // Promise<Settings>
  ] as const);

  // TypeScript推断正确类型
  const firstUser: User = users[0];
  const firstPost: Post = posts[0];
}

Promise.allSettled

interface SettledResult<T> {
  status: 'fulfilled' | 'rejected';
  value?: T;
  reason?: Error;
}

async function fetchAllUserData(
  userIds: string[]
): Promise<Array<SettledResult<User>>> {
  const results = await Promise.allSettled(
    userIds.map((id) => fetchUserData(id))
  );

  return results.map((result) => {
    if (result.status === 'fulfilled') {
      return { status: 'fulfilled', value: result.value };
    } else {
      return { status: 'rejected', reason: result.reason };
    }
  });
}

// 用法
const results = await fetchAllUserData(['1', '2', '3']);
const successful = results.filter((r) => r.status === 'fulfilled');
const failed = results.filter((r) => r.status === 'rejected');

console.log(`${successful.length} 个成功,${failed.length} 个失败`);

Promise.race 和 Promise.any

// Promise.race - 第一个结算(fulfill或reject)
async function fetchWithTimeout<T>(
  promise: Promise<T>,
  timeoutMs: number
): Promise<T> {
  const timeout = new Promise<never>((_, reject) => {
    setTimeout(() => reject(new Error('操作超时')), timeoutMs);
  });

  return Promise.race([promise, timeout]);
}

// 用法
const data = await fetchWithTimeout(fetchUserData('123'), 5000);

// Promise.any - 第一个fulfill(忽略rejections)
async function fetchFromFastestServer<T>(
  urls: string[]
): Promise<T> {
  const fetchPromises = urls.map((url) => fetchWithErrorHandling<T>(url));

  try {
    return await Promise.any(fetchPromises);
  } catch (error) {
    throw new Error('所有服务器都未能响应');
  }
}

异步迭代器和生成器

基本异步迭代器

interface AsyncIteratorResult<T> {
  value: T;
  done: boolean;
}

async function* numberGenerator(max: number): AsyncGenerator<number> {
  for (let i = 0; i < max; i++) {
    await delay(100);
    yield i;
  }
}

// 使用异步迭代器
async function consumeNumbers() {
  for await (const num of numberGenerator(5)) {
    console.log(num);
  }
}

异步可迭代数据流

interface DataChunk {
  data: string;
  timestamp: number;
}

class AsyncDataStream implements AsyncIterable<DataChunk> {
  constructor(private source: string[]) {}

  async *[Symbol.asyncIterator](): AsyncGenerator<DataChunk> {
    for (const data of this.source) {
      await delay(100);
      yield {
        data,
        timestamp: Date.now(),
      };
    }
  }
}

// 用法
async function processStream() {
  const stream = new AsyncDataStream(['chunk1', 'chunk2', 'chunk3']);

  for await (const chunk of stream) {
    console.log(`在 ${chunk.timestamp} 接收:${chunk.data}`);
  }
}

转换异步可迭代对象

async function* mapAsync<T, R>(
  iterable: AsyncIterable<T>,
  mapper: (value: T) => Promise<R> | R
): AsyncGenerator<R> {
  for await (const value of iterable) {
    yield await mapper(value);
  }
}

async function* filterAsync<T>(
  iterable: AsyncIterable<T>,
  predicate: (value: T) => Promise<boolean> | boolean
): AsyncGenerator<T> {
  for await (const value of iterable) {
    if (await predicate(value)) {
      yield value;
    }
  }
}

// 用法
async function transformData() {
  const stream = new AsyncDataStream(['1', '2', '3', '4', '5']);

  const numbers = mapAsync(stream, (chunk) => parseInt(chunk.data));
  const evenNumbers = filterAsync(numbers, (n) => n % 2 === 0);

  for await (const num of evenNumbers) {
    console.log(num); // 2, 4
  }
}

观察者模式

简单观察者实现

type Observer<T> = (value: T) => void;
type Unsubscribe = () => void;

class Observable<T> {
  private observers: Set<Observer<T>> = new Set();

  subscribe(observer: Observer<T>): Unsubscribe {
    this.observers.add(observer);
    return () => {
      this.observers.delete(observer);
    };
  }

  next(value: T): void {
    this.observers.forEach((observer) => observer(value));
  }

  pipe<R>(operator: (obs: Observable<T>) => Observable<R>): Observable<R> {
    return operator(this);
  }
}

// 操作符
function map<T, R>(mapper: (value: T) => R) {
  return (source: Observable<T>): Observable<R> => {
    const result = new Observable<R>();

    source.subscribe((value) => {
      result.next(mapper(value));
    });

    return result;
  };
}

// 用法
const numbers = new Observable<number>();
const doubled = numbers.pipe(map((n) => n * 2));

doubled.subscribe((value) => console.log(value));
numbers.next(5); // 10

异步观察者

interface AsyncObserver<T> {
  next: (value: T) => Promise<void> | void;
  error?: (error: Error) => Promise<void> | void;
  complete?: () => Promise<void> | void;
}

class AsyncObservable<T> {
  private observers: Set<AsyncObserver<T>> = new Set();

  subscribe(observer: AsyncObserver<T>): Unsubscribe {
    this.observers.add(observer);
    return () => {
      this.observers.delete(observer);
    };
  }

  async next(value: T): Promise<void> {
    await Promise.all(
      Array.from(this.observers).map((obs) => obs.next(value))
    );
  }

  async error(error: Error): Promise<void> {
    await Promise.all(
      Array.from(this.observers)
        .filter((obs) => obs.error)
        .map((obs) => obs.error!(error))
    );
  }

  async complete(): Promise<void> {
    await Promise.all(
      Array.from(this.observers)
        .filter((obs) => obs.complete)
        .map((obs) => obs.complete!())
    );
  }
}

取消模式

使用AbortController进行取消

async function fetchWithCancellation(
  url: string,
  signal: AbortSignal
): Promise<Response> {
  const response = await fetch(url, { signal });

  if (signal.aborted) {
    throw new Error('请求被取消');
  }

  return response;
}

// 用法
const controller = new AbortController();

// 开始获取
const fetchPromise = fetchWithCancellation(
  'https://api.example.com/data',
  controller.signal
);

// 5秒后取消
setTimeout(() => controller.abort(), 5000);

try {
  const response = await fetchPromise;
} catch (error) {
  if (error instanceof Error && error.name === 'AbortError') {
    console.log('请求被取消');
  }
}

可取消的Promise包装器

interface CancellablePromise<T> extends Promise<T> {
  cancel: () => void;
}

function makeCancellable<T>(
  promise: Promise<T>
): CancellablePromise<T> {
  let cancelled = false;

  const wrappedPromise = new Promise<T>((resolve, reject) => {
    promise
      .then((value) => {
        if (!cancelled) {
          resolve(value);
        }
      })
      .catch((error) => {
        if (!cancelled) {
          reject(error);
        }
      });
  }) as CancellablePromise<T>;

  wrappedPromise.cancel = () => {
    cancelled = true;
  };

  return wrappedPromise;
}

// 用法
const cancellable = makeCancellable(fetchUserData('123'));

setTimeout(() => {
  cancellable.cancel();
}, 1000);

重试和超时模式

指数退避重试

interface RetryOptions {
  maxAttempts: number;
  baseDelay: number;
  maxDelay: number;
  backoffMultiplier: number;
}

async function retry<T>(
  fn: () => Promise<T>,
  options: RetryOptions
): Promise<T> {
  let lastError: Error | undefined;

  for (let attempt = 0; attempt < options.maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error instanceof Error ? error : new Error(String(error));

      if (attempt < options.maxAttempts - 1) {
        const delayMs = Math.min(
          options.baseDelay * Math.pow(options.backoffMultiplier, attempt),
          options.maxDelay
        );

        console.log(`尝试 ${attempt + 1} 失败,${delayMs}毫秒后重试`);
        await delay(delayMs);
      }
    }
  }

  throw new Error(
    `经过 ${options.maxAttempts} 次尝试后失败:${lastError?.message ?? '未知错误'}`
  );
}

// 用法
const data = await retry(
  () => fetchUserData('123'),
  {
    maxAttempts: 3,
    baseDelay: 1000,
    maxDelay: 5000,
    backoffMultiplier: 2,
  }
);

条件重试

type RetryPredicate = (error: Error, attempt: number) => boolean;

async function retryWhen<T>(
  fn: () => Promise<T>,
  shouldRetry: RetryPredicate,
  maxAttempts: number
): Promise<T> {
  let lastError: Error | undefined;

  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error instanceof Error ? error : new Error(String(error));

      if (attempt < maxAttempts - 1 && shouldRetry(lastError, attempt)) {
        await delay(1000 * (attempt + 1));
      } else {
        throw lastError;
      }
    }
  }

  throw lastError!;
}

// 用法:仅在网络错误时重试
const data = await retryWhen(
  () => fetchUserData('123'),
  (error) => error instanceof NetworkError,
  3
);

超时模式

class TimeoutError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'TimeoutError';
  }
}

async function withTimeout<T>(
  promise: Promise<T>,
  timeoutMs: number,
  message = '操作超时'
): Promise<T> {
  let timeoutId: NodeJS.Timeout;

  const timeoutPromise = new Promise<never>((_, reject) => {
    timeoutId = setTimeout(() => {
      reject(new TimeoutError(message));
    }, timeoutMs);
  });

  try {
    return await Promise.race([promise, timeoutPromise]);
  } finally {
    clearTimeout(timeoutId!);
  }
}

// 结合重试和超时
async function retryWithTimeout<T>(
  fn: () => Promise<T>,
  options: RetryOptions & { timeout: number }
): Promise<T> {
  return retry(
    () => withTimeout(fn(), options.timeout),
    options
  );
}

异步类型推断

推断Promise类型

// 提取Promise类型
type Awaited<T> = T extends Promise<infer U> ? U : T;

// 用法
type UserPromise = Promise<User>;
type UserType = Awaited<UserPromise>; // User

// 对于函数
type ReturnTypeAsync<T extends (...args: any) => any> =
  Awaited<ReturnType<T>>;

async function getUser(): Promise<User> {
  return { id: '1', name: 'John', email: 'john@example.com' };
}

type UserFromFunction = ReturnTypeAsync<typeof getUser>; // User

类型化的异步函数工具

type AsyncFn<Args extends any[], R> = (...args: Args) => Promise<R>;

// 类型安全的异步管道
function composeAsync<A, B, C>(
  f: AsyncFn<[A], B>,
  g: AsyncFn<[B], C>
): AsyncFn<[A], C> {
  return async (a: A) => {
    const b = await f(a);
    return g(b);
  };
}

// 记忆化异步函数
function memoizeAsync<Args extends any[], R>(
  fn: AsyncFn<Args, R>
): AsyncFn<Args, R> {
  const cache = new Map<string, Promise<R>>();

  return async (...args: Args) => {
    const key = JSON.stringify(args);

    if (cache.has(key)) {
      return cache.get(key)!;
    }

    const promise = fn(...args);
    cache.set(key, promise);

    try {
      return await promise;
    } catch (error) {
      cache.delete(key);
      throw error;
    }
  };
}

最佳实践

  1. 始终处理错误:使用try-catch和async/await或.catch()处理Promise。永远不要让错误在异步代码中未处理。

  2. 避免混合范式:对于给定流程,选择要么Promise链式调用,要么async/await。混合它们会使代码更难阅读和维护。

  3. 使用Promise.all进行并行操作:当操作独立时,使用Promise.all并行运行它们,而不是顺序执行。

  4. 类型化Promise返回值:始终显式类型化异步函数和Promise的返回值,以获得更好的类型安全和IDE支持。

  5. 处理竞态条件:在异步代码中要小心共享状态。使用适当的同步或不可变数据结构。

  6. 为网络请求设置超时:始终添加超时以防止挂起请求。使用AbortController或Promise.race。

  7. 实现适当的清理:使用finally块或try/finally确保清理代码无论成功或失败都运行。

  8. 避免在构造函数中使用异步:构造函数不能是异步的。使用工厂函数或初始化方法代替。

  9. 使用AbortController进行取消:对于更好的浏览器/Node.js兼容性,优先使用标准AbortController而非自定义取消。

  10. 记录异步行为:清晰记录异步函数做什么、返回什么以及可能抛出什么错误。

常见陷阱

  1. 忘记await:忘记在异步函数上使用await会返回Promise而不是解析值,导致类型错误和bug。

  2. 串行时并行更好:当操作可以并行运行时,在循环中使用await会导致性能差。

  3. 未处理的Promise拒绝:不捕获Promise或异步函数中的错误会导致Node.js应用程序崩溃或静默失败。

  4. 悬空Promises:不等待或不处理Promises(即发即弃)可能导致未处理的拒绝和竞态条件。

  5. Promise构造器反模式:将已经Promise化的函数包装在new Promise中是不必要的,会增加复杂性。

  6. 异步IIFE错误:忘记立即调用的异步函数的await或错误处理会导致静默失败。

  7. 错误类型假设错误:假设所有错误都是Error实例。使用正确的类型检查或类型守卫。

  8. 异步迭代器中的内存泄漏:不适当清理异步迭代器可能导致内存泄漏,尤其是无限流。

  9. 忽略取消:不为长时间运行的操作实现取消会浪费资源并降低用户体验。

  10. 过度使用异步:当存在同步替代方案时,使所有内容都异步会增加不必要的复杂性和性能开销。

何时使用此技能

在需要时使用TypeScript异步模式:

  • 进行API调用或与外部服务交互
  • 执行I/O操作(文件系统、数据库、网络)
  • 构建具有流数据的实时应用程序
  • 处理触发异步操作的用户交互
  • 实现后台任务或计划作业
  • 使用WebSockets或服务器发送事件
  • 异步处理大型数据集
  • 构建不阻塞主线程的响应式UI
  • 为不可靠的操作实现重试逻辑
  • 创建可组合的异步工作流

此技能对于全栈开发人员、与API合作的前端工程师、构建服务的后端开发人员以及任何构建现代JavaScript/TypeScript应用程序的人来说都至关重要。

资源

文档

书籍和文章

  • “You Don’t Know JS: Async & Performance” by Kyle Simpson
  • “JavaScript: The Definitive Guide” by David Flanagan
  • “Async JavaScript” by Trevor Burnham

工具