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;
}
};
}
最佳实践
-
始终处理错误:使用try-catch和async/await或.catch()处理Promise。永远不要让错误在异步代码中未处理。
-
避免混合范式:对于给定流程,选择要么Promise链式调用,要么async/await。混合它们会使代码更难阅读和维护。
-
使用Promise.all进行并行操作:当操作独立时,使用Promise.all并行运行它们,而不是顺序执行。
-
类型化Promise返回值:始终显式类型化异步函数和Promise的返回值,以获得更好的类型安全和IDE支持。
-
处理竞态条件:在异步代码中要小心共享状态。使用适当的同步或不可变数据结构。
-
为网络请求设置超时:始终添加超时以防止挂起请求。使用AbortController或Promise.race。
-
实现适当的清理:使用finally块或try/finally确保清理代码无论成功或失败都运行。
-
避免在构造函数中使用异步:构造函数不能是异步的。使用工厂函数或初始化方法代替。
-
使用AbortController进行取消:对于更好的浏览器/Node.js兼容性,优先使用标准AbortController而非自定义取消。
-
记录异步行为:清晰记录异步函数做什么、返回什么以及可能抛出什么错误。
常见陷阱
-
忘记await:忘记在异步函数上使用await会返回Promise而不是解析值,导致类型错误和bug。
-
串行时并行更好:当操作可以并行运行时,在循环中使用await会导致性能差。
-
未处理的Promise拒绝:不捕获Promise或异步函数中的错误会导致Node.js应用程序崩溃或静默失败。
-
悬空Promises:不等待或不处理Promises(即发即弃)可能导致未处理的拒绝和竞态条件。
-
Promise构造器反模式:将已经Promise化的函数包装在new Promise中是不必要的,会增加复杂性。
-
异步IIFE错误:忘记立即调用的异步函数的await或错误处理会导致静默失败。
-
错误类型假设错误:假设所有错误都是Error实例。使用正确的类型检查或类型守卫。
-
异步迭代器中的内存泄漏:不适当清理异步迭代器可能导致内存泄漏,尤其是无限流。
-
忽略取消:不为长时间运行的操作实现取消会浪费资源并降低用户体验。
-
过度使用异步:当存在同步替代方案时,使所有内容都异步会增加不必要的复杂性和性能开销。
何时使用此技能
在需要时使用TypeScript异步模式:
- 进行API调用或与外部服务交互
- 执行I/O操作(文件系统、数据库、网络)
- 构建具有流数据的实时应用程序
- 处理触发异步操作的用户交互
- 实现后台任务或计划作业
- 使用WebSockets或服务器发送事件
- 异步处理大型数据集
- 构建不阻塞主线程的响应式UI
- 为不可靠的操作实现重试逻辑
- 创建可组合的异步工作流
此技能对于全栈开发人员、与API合作的前端工程师、构建服务的后端开发人员以及任何构建现代JavaScript/TypeScript应用程序的人来说都至关重要。
资源
文档
- MDN Web文档 - Async/Await: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
- TypeScript手册 - 异步函数: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-7.html
- JavaScript Promises - MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
书籍和文章
- “You Don’t Know JS: Async & Performance” by Kyle Simpson
- “JavaScript: The Definitive Guide” by David Flanagan
- “Async JavaScript” by Trevor Burnham
库
- RxJS: https://rxjs.dev/ - JavaScript的响应式扩展
- p-limit: https://github.com/sindresorhus/p-limit - Promise并发控制
- p-retry: https://github.com/sindresorhus/p-retry - 重试失败的promises
- abort-controller: https://www.npmjs.com/package/abort-controller - AbortController polyfill
工具
- TypeScript Playground: https://www.typescriptlang.org/play
- Chrome DevTools - 异步堆栈跟踪
- Node.js --trace-warnings 标志用于调试未处理的拒绝