typescript-async-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTypeScript Async Patterns
TypeScript异步模式
Master asynchronous programming patterns in TypeScript, including Promises,
async/await, error handling, async iterators, and advanced patterns for
building robust async applications.
掌握TypeScript中的异步编程模式,包括Promises、async/await、错误处理、异步迭代器,以及构建健壮异步应用的高级模式。
Promises and async/await
Promises和async/await
Basic Promise Creation
基础Promise创建
typescript
// Creating a Promise
function delay(ms: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
// Promise with value
function fetchUserData(userId: string): Promise<User> {
return new Promise((resolve, reject) => {
// Simulated API call
setTimeout(() => {
if (userId) {
resolve({ id: userId, name: 'John Doe' });
} else {
reject(new Error('Invalid user ID'));
}
}, 1000);
});
}
// Using the Promise
fetchUserData('123')
.then((user) => {
console.log(user.name);
})
.catch((error) => {
console.error('Error:', error.message);
});typescript
// Creating a Promise
function delay(ms: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
// Promise with value
function fetchUserData(userId: string): Promise<User> {
return new Promise((resolve, reject) => {
// Simulated API call
setTimeout(() => {
if (userId) {
resolve({ id: userId, name: 'John Doe' });
} else {
reject(new Error('Invalid user ID'));
}
}, 1000);
});
}
// Using the Promise
fetchUserData('123')
.then((user) => {
console.log(user.name);
})
.catch((error) => {
console.error('Error:', error.message);
});async/await Syntax
async/await语法
typescript
interface User {
id: string;
name: string;
email: string;
}
interface Post {
id: string;
userId: string;
title: string;
content: string;
}
// Async function declaration
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('Failed to fetch user posts:', error);
throw error;
}
}
// Async arrow function
const getUserProfile = async (userId: string): Promise<User> => {
const user = await fetchUserData(userId);
return user;
};
// Using async/await
async function main() {
const posts = await getUserPosts('123');
console.log(`Found ${posts.length} posts`);
}typescript
interface User {
id: string;
name: string;
email: string;
}
interface Post {
id: string;
userId: string;
title: string;
content: string;
}
// Async function declaration
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('Failed to fetch user posts:', error);
throw error;
}
}
// Async arrow function
const getUserProfile = async (userId: string): Promise<User> => {
const user = await fetchUserData(userId);
return user;
};
// Using async/await
async function main() {
const posts = await getUserPosts('123');
console.log(`Found ${posts.length} posts`);
}Type-Safe Promise Wrappers
类型安全的Promise包装器
typescript
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)),
};
}
}
// Usage
async function example() {
const result = await safeAsync(fetchUserData('123'));
if (result.success) {
console.log(result.data.name);
} else {
console.error(result.error.message);
}
}typescript
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)),
};
}
}
// Usage
async function example() {
const result = await safeAsync(fetchUserData('123'));
if (result.success) {
console.log(result.data.name);
} else {
console.error(result.error.message);
}
}Promise Chaining and Composition
Promise链式调用与组合
Chaining Promises
链式调用Promises
typescript
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 error! status: ${response.status}`);
}
return response.json();
})
.then((data) => ({
data,
status: 200,
}));
}
// Chaining multiple async operations
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) => `Found ${filteredPosts.length} TypeScript posts`)
.catch((error) => {
console.error('Error in chain:', error);
return 'Failed to process user data';
});
}typescript
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 error! status: ${response.status}`);
}
return response.json();
})
.then((data) => ({
data,
status: 200,
}));
}
// Chaining multiple async operations
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) => `Found ${filteredPosts.length} TypeScript posts`)
.catch((error) => {
console.error('Error in chain:', error);
return 'Failed to process user data';
});
}Composing Async Functions
异步函数组合
typescript
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);
};
}
// Usage
const getUserId = async (username: string): Promise<string> => {
// Look up user ID
return '123';
};
const getUserData = async (userId: string): Promise<User> => {
return fetchUserData(userId);
};
const getUserByUsername = pipe(getUserId, getUserData);
// Use the composed function
const user = await getUserByUsername('johndoe');typescript
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);
};
}
// Usage
const getUserId = async (username: string): Promise<string> => {
// Look up user ID
return '123';
};
const getUserData = async (userId: string): Promise<User> => {
return fetchUserData(userId);
};
const getUserByUsername = pipe(getUserId, getUserData);
// Use the composed function
const user = await getUserByUsername('johndoe');Error Handling in Async Code
异步代码错误处理
Try-Catch with async/await
async/await搭配Try-Catch
typescript
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 error! status: ${response.status}`,
response.status
);
}
const data = await response.json();
return data;
} catch (error) {
if (error instanceof ApiError) {
console.error(`API Error ${error.statusCode}: ${error.message}`);
} else if (error instanceof TypeError) {
console.error('Network error:', error.message);
} else {
console.error('Unknown error:', error);
}
throw error;
}
}typescript
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 error! status: ${response.status}`,
response.status
);
}
const data = await response.json();
return data;
} catch (error) {
if (error instanceof ApiError) {
console.error(`API Error ${error.statusCode}: ${error.message}`);
} else if (error instanceof TypeError) {
console.error('Network error:', error.message);
} else {
console.error('Unknown error:', error);
}
throw error;
}
}Error Recovery Patterns
错误恢复模式
typescript
async function fetchWithFallback<T>(
primaryUrl: string,
fallbackUrl: string
): Promise<T> {
try {
return await fetchWithErrorHandling<T>(primaryUrl);
} catch (error) {
console.warn('Primary fetch failed, trying fallback');
return await fetchWithErrorHandling<T>(fallbackUrl);
}
}
// Multiple fallbacks
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(`Failed to fetch from ${url}, trying next...`);
}
}
throw new Error(
`All fetches failed. Last error: ${lastError?.message ?? 'Unknown'}`
);
}typescript
async function fetchWithFallback<T>(
primaryUrl: string,
fallbackUrl: string
): Promise<T> {
try {
return await fetchWithErrorHandling<T>(primaryUrl);
} catch (error) {
console.warn('Primary fetch failed, trying fallback');
return await fetchWithErrorHandling<T>(fallbackUrl);
}
}
// Multiple fallbacks
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(`Failed to fetch from ${url}, trying next...`);
}
}
throw new Error(
`All fetches failed. Last error: ${lastError?.message ?? 'Unknown'}`
);
}Typed Error Handling
带类型的错误处理
typescript
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(`Validation error in field ${error.field}: ${error.message}`);
} else if (error instanceof NetworkError) {
console.error(`Network error for ${error.url}: ${error.message}`);
} else if (error instanceof Error) {
console.error(`Unexpected error: ${error.message}`);
}
throw error;
}
}typescript
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(`Validation error in field ${error.field}: ${error.message}`);
} else if (error instanceof NetworkError) {
console.error(`Network error for ${error.url}: ${error.message}`);
} else if (error instanceof Error) {
console.error(`Unexpected error: ${error.message}`);
}
throw error;
}
}Promise Combinators
Promise组合器
Promise.all
Promise.all
typescript
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 };
}
// Type-safe Promise.all with tuple
async function fetchMultipleResources() {
const [users, posts, settings] = await Promise.all([
fetchUsers(), // Promise<User[]>
fetchPosts(), // Promise<Post[]>
fetchSettings(), // Promise<Settings>
] as const);
// TypeScript infers correct types
const firstUser: User = users[0];
const firstPost: Post = posts[0];
}typescript
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 };
}
// Type-safe Promise.all with tuple
async function fetchMultipleResources() {
const [users, posts, settings] = await Promise.all([
fetchUsers(), // Promise<User[]>
fetchPosts(), // Promise<Post[]>
fetchSettings(), // Promise<Settings>
] as const);
// TypeScript infers correct types
const firstUser: User = users[0];
const firstPost: Post = posts[0];
}Promise.allSettled
Promise.allSettled
typescript
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 };
}
});
}
// Usage
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} succeeded, ${failed.length} failed`);typescript
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 };
}
});
}
// Usage
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} succeeded, ${failed.length} failed`);Promise.race and Promise.any
Promise.race和Promise.any
typescript
// Promise.race - first to settle (fulfill or reject)
async function fetchWithTimeout<T>(
promise: Promise<T>,
timeoutMs: number
): Promise<T> {
const timeout = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error('Operation timed out')), timeoutMs);
});
return Promise.race([promise, timeout]);
}
// Usage
const data = await fetchWithTimeout(fetchUserData('123'), 5000);
// Promise.any - first to fulfill (ignores 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('All servers failed to respond');
}
}typescript
// Promise.race - first to settle (fulfill or reject)
async function fetchWithTimeout<T>(
promise: Promise<T>,
timeoutMs: number
): Promise<T> {
const timeout = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error('Operation timed out')), timeoutMs);
});
return Promise.race([promise, timeout]);
}
// Usage
const data = await fetchWithTimeout(fetchUserData('123'), 5000);
// Promise.any - first to fulfill (ignores 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('All servers failed to respond');
}
}Async Iterators and Generators
异步迭代器与生成器
Basic Async Iterators
基础异步迭代器
typescript
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;
}
}
// Using async iterator
async function consumeNumbers() {
for await (const num of numberGenerator(5)) {
console.log(num);
}
}typescript
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;
}
}
// Using async iterator
async function consumeNumbers() {
for await (const num of numberGenerator(5)) {
console.log(num);
}
}Async Iterable Data Streams
异步可迭代数据流
typescript
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(),
};
}
}
}
// Usage
async function processStream() {
const stream = new AsyncDataStream(['chunk1', 'chunk2', 'chunk3']);
for await (const chunk of stream) {
console.log(`Received at ${chunk.timestamp}: ${chunk.data}`);
}
}typescript
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(),
};
}
}
}
// Usage
async function processStream() {
const stream = new AsyncDataStream(['chunk1', 'chunk2', 'chunk3']);
for await (const chunk of stream) {
console.log(`Received at ${chunk.timestamp}: ${chunk.data}`);
}
}Transforming Async Iterables
转换异步可迭代对象
typescript
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;
}
}
}
// Usage
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
}
}typescript
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;
}
}
}
// Usage
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
}
}Observable Patterns
观察者模式
Simple Observable Implementation
简单观察者实现
typescript
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);
}
}
// Operator
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;
};
}
// Usage
const numbers = new Observable<number>();
const doubled = numbers.pipe(map((n) => n * 2));
doubled.subscribe((value) => console.log(value));
numbers.next(5); // 10typescript
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);
}
}
// Operator
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;
};
}
// Usage
const numbers = new Observable<number>();
const doubled = numbers.pipe(map((n) => n * 2));
doubled.subscribe((value) => console.log(value));
numbers.next(5); // 10Async Observable
异步观察者
typescript
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!())
);
}
}typescript
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!())
);
}
}Cancellation Patterns
取消模式
AbortController for Cancellation
使用AbortController实现取消
typescript
async function fetchWithCancellation(
url: string,
signal: AbortSignal
): Promise<Response> {
const response = await fetch(url, { signal });
if (signal.aborted) {
throw new Error('Request was cancelled');
}
return response;
}
// Usage
const controller = new AbortController();
// Start fetch
const fetchPromise = fetchWithCancellation(
'https://api.example.com/data',
controller.signal
);
// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000);
try {
const response = await fetchPromise;
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
console.log('Request was cancelled');
}
}typescript
async function fetchWithCancellation(
url: string,
signal: AbortSignal
): Promise<Response> {
const response = await fetch(url, { signal });
if (signal.aborted) {
throw new Error('Request was cancelled');
}
return response;
}
// Usage
const controller = new AbortController();
// Start fetch
const fetchPromise = fetchWithCancellation(
'https://api.example.com/data',
controller.signal
);
// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000);
try {
const response = await fetchPromise;
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
console.log('Request was cancelled');
}
}Cancellable Promise Wrapper
可取消Promise包装器
typescript
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;
}
// Usage
const cancellable = makeCancellable(fetchUserData('123'));
setTimeout(() => {
cancellable.cancel();
}, 1000);typescript
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;
}
// Usage
const cancellable = makeCancellable(fetchUserData('123'));
setTimeout(() => {
cancellable.cancel();
}, 1000);Retry and Timeout Patterns
重试与超时模式
Retry with Exponential Backoff
指数退避重试
typescript
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 ${attempt + 1} failed, retrying in ${delayMs}ms`);
await delay(delayMs);
}
}
}
throw new Error(
`Failed after ${options.maxAttempts} attempts: ${lastError?.message ?? 'Unknown error'}`
);
}
// Usage
const data = await retry(
() => fetchUserData('123'),
{
maxAttempts: 3,
baseDelay: 1000,
maxDelay: 5000,
backoffMultiplier: 2,
}
);typescript
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 ${attempt + 1} failed, retrying in ${delayMs}ms`);
await delay(delayMs);
}
}
}
throw new Error(
`Failed after ${options.maxAttempts} attempts: ${lastError?.message ?? 'Unknown error'}`
);
}
// Usage
const data = await retry(
() => fetchUserData('123'),
{
maxAttempts: 3,
baseDelay: 1000,
maxDelay: 5000,
backoffMultiplier: 2,
}
);Conditional Retry
条件重试
typescript
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!;
}
// Usage: Only retry on network errors
const data = await retryWhen(
() => fetchUserData('123'),
(error) => error instanceof NetworkError,
3
);typescript
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!;
}
// Usage: Only retry on network errors
const data = await retryWhen(
() => fetchUserData('123'),
(error) => error instanceof NetworkError,
3
);Timeout Pattern
超时模式
typescript
class TimeoutError extends Error {
constructor(message: string) {
super(message);
this.name = 'TimeoutError';
}
}
async function withTimeout<T>(
promise: Promise<T>,
timeoutMs: number,
message = 'Operation timed out'
): 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!);
}
}
// Combined retry with timeout
async function retryWithTimeout<T>(
fn: () => Promise<T>,
options: RetryOptions & { timeout: number }
): Promise<T> {
return retry(
() => withTimeout(fn(), options.timeout),
options
);
}typescript
class TimeoutError extends Error {
constructor(message: string) {
super(message);
this.name = 'TimeoutError';
}
}
async function withTimeout<T>(
promise: Promise<T>,
timeoutMs: number,
message = 'Operation timed out'
): 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!);
}
}
// Combined retry with timeout
async function retryWithTimeout<T>(
fn: () => Promise<T>,
options: RetryOptions & { timeout: number }
): Promise<T> {
return retry(
() => withTimeout(fn(), options.timeout),
options
);
}Async Type Inference
异步类型推断
Inferring Promise Types
推断Promise类型
typescript
// Extract Promise type
type Awaited<T> = T extends Promise<infer U> ? U : T;
// Usage
type UserPromise = Promise<User>;
type UserType = Awaited<UserPromise>; // User
// For functions
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>; // Usertypescript
// Extract Promise type
type Awaited<T> = T extends Promise<infer U> ? U : T;
// Usage
type UserPromise = Promise<User>;
type UserType = Awaited<UserPromise>; // User
// For functions
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>; // UserTyped Async Function Utilities
带类型的异步函数工具
typescript
type AsyncFn<Args extends any[], R> = (...args: Args) => Promise<R>;
// Type-safe async pipe
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);
};
}
// Memoize async function
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;
}
};
}typescript
type AsyncFn<Args extends any[], R> = (...args: Args) => Promise<R>;
// Type-safe async pipe
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);
};
}
// Memoize async function
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;
}
};
}Best Practices
最佳实践
-
Always Handle Errors: Use try-catch with async/await or .catch() with Promises. Never let errors go unhandled in async code.
-
Avoid Mixing Paradigms: Choose either Promise chains or async/await for a given flow. Mixing them makes code harder to read and maintain.
-
Use Promise.all for Parallel Operations: When operations are independent, use Promise.all to run them in parallel rather than sequentially.
-
Type Promise Return Values: Always explicitly type the return value of async functions and Promises for better type safety and IDE support.
-
Handle Race Conditions: Be careful with shared state in async code. Use proper synchronization or immutable data structures.
-
Set Timeouts for Network Requests: Always add timeouts to prevent hanging requests. Use AbortController or Promise.race.
-
Implement Proper Cleanup: Use finally blocks or try/finally to ensure cleanup code runs regardless of success or failure.
-
Avoid Async in Constructors: Constructors cannot be async. Use factory functions or initialization methods instead.
-
Use AbortController for Cancellation: Prefer standard AbortController over custom cancellation for better browser/Node.js compatibility.
-
Document Async Behavior: Clearly document what async functions do, what they return, and what errors they might throw.
-
始终处理错误:async/await搭配try-catch使用,或Promise搭配.catch()使用。永远不要让异步代码中的错误未被处理。
-
避免混合编程范式:同一个流程中要么选择Promise链式调用,要么选择async/await。混合使用会降低代码可读性和可维护性。
-
并行操作使用Promise.all:当多个操作相互独立时,使用Promise.all并行执行,而非顺序执行。
-
为Promise返回值添加类型:始终为异步函数和Promise的返回值显式标注类型,以获得更好的类型安全和IDE支持。
-
处理竞态条件:异步代码中操作共享状态时要小心,使用合适的同步机制或不可变数据结构。
-
为网络请求设置超时:始终添加超时避免请求挂起,可使用AbortController或Promise.race实现。
-
实现合理的清理逻辑:使用finally块或try/finally确保清理代码无论请求成功或失败都会执行。
-
避免在构造函数中使用异步逻辑:构造函数不能是异步的,可使用工厂函数或初始化方法替代。
-
使用AbortController实现取消:优先使用标准的AbortController而非自定义取消逻辑,以获得更好的浏览器/Node.js兼容性。
-
文档说明异步行为:清晰记录异步函数的功能、返回值以及可能抛出的错误。
Common Pitfalls
常见陷阱
-
Forgetting await: Forgetting await on async functions returns a Promise instead of the resolved value, causing type errors and bugs.
-
Sequential When Parallel Is Better: Using await in loops when operations could run in parallel leads to poor performance.
-
Unhandled Promise Rejections: Not catching errors in Promises or async functions can crash Node.js applications or cause silent failures.
-
Floating Promises: Not awaiting or handling Promises (fire-and-forget) can cause unhandled rejections and race conditions.
-
Promise Constructor Anti-pattern: Wrapping already-promisified functions in new Promise is unnecessary and adds complexity.
-
Async IIFE Mistakes: Forgetting to await or handle errors from immediately-invoked async functions causes silent failures.
-
Wrong Error Type Assumptions: Assuming all errors are Error instances. Use proper type checking or type guards.
-
Memory Leaks in Async Iterators: Not properly cleaning up async iterators can cause memory leaks, especially with infinite streams.
-
Ignoring Cancellation: Not implementing cancellation for long-running operations wastes resources and degrades user experience.
-
Over-Using Async: Making everything async when synchronous alternatives exist adds unnecessary complexity and performance overhead.
-
忘记添加await:调用异步函数时忘记加await会返回Promise而非 resolve后的值,导致类型错误和bug。
-
可并行时却顺序执行:在循环中使用await,而实际上操作可以并行执行,会导致性能低下。
-
未处理的Promise拒绝:未捕获Promise或异步函数中的错误可能导致Node.js应用崩溃或静默失败。
-
悬空Promise:不对Promise做await或处理(即发即忘)会导致未处理的拒绝和竞态条件。
-
Promise构造器反模式:将已经支持Promise的函数包装在new Promise中是不必要的,只会增加复杂度。
-
异步立即执行函数错误:忘记对立即调用的异步函数加await或处理错误,会导致静默失败。
-
错误类型假设错误:默认所有错误都是Error实例,应使用合适的类型检查或类型守卫。
-
异步迭代器内存泄漏:未正确清理异步迭代器会导致内存泄漏,尤其是无限流场景下。
-
忽略取消逻辑:长时间运行的操作未实现取消逻辑会浪费资源,降低用户体验。
-
过度使用异步:当存在同步替代方案时仍将所有逻辑改为异步,会增加不必要的复杂度和性能开销。
When to Use This Skill
何时使用本技能
Use TypeScript async patterns when you need to:
- Make API calls or interact with external services
- Perform I/O operations (file system, database, network)
- Build real-time applications with streaming data
- Handle user interactions that trigger async operations
- Implement background tasks or scheduled jobs
- Work with WebSockets or Server-Sent Events
- Process large datasets asynchronously
- Build responsive UIs that don't block the main thread
- Implement retry logic for unreliable operations
- Create composable async workflows
This skill is essential for full-stack developers, frontend engineers
working with APIs, backend developers building services, and anyone building
modern JavaScript/TypeScript applications.
当你需要完成以下操作时,可以使用TypeScript异步模式:
- 发起API调用或与外部服务交互
- 执行I/O操作(文件系统、数据库、网络)
- 构建带数据流的实时应用
- 处理触发异步操作的用户交互
- 实现后台任务或定时任务
- 对接WebSockets或服务器发送事件(Server-Sent Events)
- 异步处理大型数据集
- 构建不会阻塞主线程的响应式UI
- 为不可靠的操作实现重试逻辑
- 创建可组合的异步工作流
本技能对全栈开发者、对接API的前端工程师、构建服务的后端开发者,以及所有构建现代JavaScript/TypeScript应用的开发者而言都是必备的。
Resources
资源
Documentation
文档
- MDN Web Docs - Async/Await: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
- TypeScript Handbook - Async Functions: 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
- MDN Web Docs - Async/Await: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
- TypeScript Handbook - Async Functions: 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
Books and Articles
书籍和文章
- "You Don't Know JS: Async & Performance" by Kyle Simpson
- "JavaScript: The Definitive Guide" by David Flanagan
- "Async JavaScript" by Trevor Burnham
- "You Don't Know JS: Async & Performance" by Kyle Simpson
- "JavaScript: The Definitive Guide" by David Flanagan
- "Async JavaScript" by Trevor Burnham
Libraries
库
- RxJS: https://rxjs.dev/ - Reactive Extensions for JavaScript
- p-limit: https://github.com/sindresorhus/p-limit - Promise concurrency control
- p-retry: https://github.com/sindresorhus/p-retry - Retry failed promises
- abort-controller: https://www.npmjs.com/package/abort-controller - AbortController polyfill
- RxJS: https://rxjs.dev/ - Reactive Extensions for JavaScript
- p-limit: https://github.com/sindresorhus/p-limit - Promise并发控制
- p-retry: https://github.com/sindresorhus/p-retry - 重试失败的promise
- abort-controller: https://www.npmjs.com/package/abort-controller - AbortController polyfill
Tools
工具
- TypeScript Playground: https://www.typescriptlang.org/play
- Chrome DevTools - Async Stack Traces
- Node.js --trace-warnings flag for debugging unhandled rejections
- TypeScript Playground: https://www.typescriptlang.org/play
- Chrome DevTools - 异步堆栈跟踪
- Node.js --trace-warnings 标记,用于调试未处理的拒绝