Loading lesson path
Concept visual
TypeScript enhances JavaScript's asynchronous capabilities with static typing, making your async code more predictable and maintainable.
Formula
This guide covers everything from basic async/await to advanced patterns.This tutorial assumes basic knowledge of JavaScript Promises and asynchronous programming. If you're new to these concepts, check out our JavaScript Async tutorial first.
TypeScript enhances JavaScript Promises with type safety through generics. A
Formula
Promise < T >represents an asynchronous operation that will complete with a value of type T or fail with a reason of type any.
Formula
Promise < T >Formula
Promise < void >- For Promises that don't return a valueFormula
Promise < never >Formula
// Create a typed Promise that resolves to a string const fetchGreeting = (): Promise < string > => {return new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve("Hello, TypeScript!");
} else {
reject(new Error("Failed to fetch greeting"));
}
}, 1000);
});
};
// Using the Promise with proper type inference fetchGreeting().then((greeting) => {
// TypeScript knows 'greeting' is a string console.log(greeting.toUpperCase());
}).catch((error: Error) => {
console.error("Error:", error.message);
});pending → fulfilled (with value: T) // Success case pending → rejected (with reason: any) // Error case TypeScript tracks these states through the type system, ensuring you handle both success and error cases properly.
Formula
Promise < T >tells TypeScript what type the Promise will resolve to, allowing for better type checking and IDE support.
TypeScript's async/await syntax provides a cleaner way to work with Promises, making asynchronous code look and behave more like synchronous code while maintaining type safety.
: Sequential code that's easier to follow
Formula
: Use try/catch for both sync and async errorsFormula
: Easier to debug with synchronous - like stack traces: Full TypeScript type inference and checking
// Define types for our API response interface User {
id: number;
name: string;
email: string;
role: 'admin' | 'user' | 'guest';
}Formula
// Function that returns a Promise of User array async function fetchUsers(): Promise < User[]> {console.log('Fetching users...');
// Simulate API call await new Promise(resolve => setTimeout(resolve, 1000));
return [
{ id: 1, name: 'Alice', email: 'alice@example.com', role: 'admin' },
{ id: 2, name: 'Bob', email: 'bob@example.com', role: 'user' }
];
}
// Async function to process users async function processUsers() {
try {
// TypeScript knows users is User[]
const users = await fetchUsers();
console.log(`Fetched ${users.length} users`);
// Type-safe property access const adminEmails = users.filter(user => user.role === 'admin').map(user => user.email);
console.log('Admin emails:', adminEmails);
return users;
} catch (error) {
if (error instanceof Error) {
console.error('Failed to process users:', error.message);
} else {
console.error('An unknown error occurred');
}
throw error; // Re-throw to let caller handle
}
}
// Execute the async function processUsers().then(users => console.log('Processing complete')).catch(err => console.error('Processing failed:', err));All async functions in TypeScript return a Promise.The return type is automatically wrapped in a Promise:
async function getString(): string { } // Error: must return Promise async function getString(): Promise<string> { } // CorrectParallel Execution with Promise.all Run multiple async operations in parallel and wait for all to complete:
interface Product {
id: number;
name: string;
price: number;
}
async function fetchProduct(id: number): Promise<Product> {
console.log(`Fetching product ${id}...`);
await new Promise(resolve => setTimeout(resolve, Math.random() * 1000));
return { id, name: `Product ${id}`, price: Math.floor(Math.random() * 100) };
}
async function fetchMultipleProducts() {
try {
// Start all fetches in parallel const [product1, product2, product3] = await Promise.all([
fetchProduct(1), fetchProduct(2), fetchProduct(3)
]);
const total = [product1, product2, product3].reduce((sum, product) => sum + product.price, 0);
console.log(`Total price: $${total.toFixed(2)}`);
} catch (error) {
console.error('Error fetching products:', error);
}
}
fetchMultipleProducts();All async functions in TypeScript return a Promise.
The type parameter of the Promise corresponds to the return type you declare after the Promise keyword.Formula
For traditional callback - based asynchronous code, TypeScript helps ensure proper typing of the callback parameters:Formula
// Define a type for the callback type FetchCallback = (error: Error | null, data?: string) => void;// Function that takes a typed callback function fetchDataWithCallback(url: string, callback: FetchCallback): void {
// Simulate async operation setTimeout(() => {
try {
// Simulate successful response callback(null, "Response data");
} catch (error) {
callback(error instanceof Error ? error : new Error('Unknown error'));
}
}, 1000);
}
// Using the callback function fetchDataWithCallback('https://api.example.com', (error, data) => {
if (error) {
console.error('Error:', error.message);
return;
}
// TypeScript knows data is a string (or undefined)
if (data) {
console.log(data.toUpperCase());
}
});TypeScript provides powerful utility types and methods for working with multiple Promises.
Formula
These methods help you manage concurrent operations and handle their results in a type - safe way.Promise.all() - Waits for all promises to resolve Promise.race() - Returns the first settled promise Promise.allSettled() - Waits for all to settle (success or failure) Promise.any() - Returns the first fulfilled promise
Formula
Promise.all - Parallel ExecutionRun multiple promises in parallel and wait for all to complete. Fails fast if any promise rejects.
Formula
// Different types of promises const fetchUser = (id: number): Promise <{ id: number; name: string }> =>Promise.resolve({ id, name: User ${id} });
const fetchPosts = (userId: number): Promise<Array<{ id: number; title: string }>> =>
Promise.resolve([
{ id: 1, title: 'Post 1' },
{ id: 2, title: 'Post 2' }
]);
const fetchStats = (userId: number): Promise<{ views: number; likes: number }> =>
Promise.resolve({ views: 100, likes: 25 });
// Run all in parallel async function loadUserDashboard(userId: number) {
try {
const [user, posts, stats] = await Promise.all([
fetchUser(userId), fetchPosts(userId), fetchStats(userId)
]);
// TypeScript knows the types of user, posts, and stats console.log(`User: ${user.name}`);
console.log(`Posts: ${posts.length}`);
console.log(`Likes: ${stats.likes}`);
return { user, posts, stats };
} catch (error) {
console.error('Failed to load dashboard:', error);
throw error;
}
}// Execute with a user ID
loadUserDashboard(1);Formula
Promise.race - First to SettleUseful for timeouts or getting the first successful response from multiple sources.
Formula
// Helper function for timeout const timeout = (ms: number): Promise < never > =>new Promise((_, reject) =>
setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms)
);Formula
// Simulate API call with timeout async function fetchWithTimeout < T > (
promise: Promise < T >, timeoutMs: number = 5000): Promise<T> {
return Promise.race([
promise, timeout(timeoutMs).then(() => {
throw new Error(`Request timed out after ${timeoutMs}ms`);
}),
]);
}
// Usage example async function fetchUserData() {
try {
const response = await fetchWithTimeout(Formula
fetch('https://api.example.com/user/1'),3000 // 3 second timeout
);
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', (error as Error).message);
throw error;
}
}Formula
Promise.allSettled - Handle All Results
When you want to wait for all promises to complete, regardless of success or failure.
// Simulate multiple API calls with different outcomes const fetchData = async (id: number) => {// Randomly fail some requests if (Math.random() > 0.7) {
throw new Error(`Failed to fetch data for ID ${id}`);
}
return { id, data: `Data for ${id}` };
};
// Process multiple items with individual error handling async function processBatch(ids: number[]) {
const promises = ids.map(id =>
fetchData(id).then(value => ({ status: 'fulfilled' as const, value })).catch(reason => ({ status: 'rejected' as const, reason }))
);
// Wait for all to complete const results = await Promise.allSettled(promises);
// Process results const successful = results.filter((result): result is PromiseFulfilledResult<{ status: 'fulfilled', value: any }> =>
result.status === 'fulfilled' &&
result.value.status === 'fulfilled'
).map(r => r.value.value);
const failed = results.filter((result): result is PromiseRejectedResult |
PromiseFulfilledResult<{ status: 'rejected', reason: any }> => {
if (result.status === 'rejected') return true;
return result.value.status === 'rejected';
});
console.log(`Successfully processed: ${successful.length}`);
console.log(`Failed: ${failed.length}`);
return { successful, failed };
}
// Process a batch of IDs processBatch([1, 2, 3, 4, 5]);Promise.all with an array of promises that have different types, TypeScript will infer the result type as an array of the union of all possible types. For more precise typing, you may need to use type assertions or define the expected structure.
Formula
TypeScript provides powerful tools for type - safe error handling in asynchronous code.Let's explore different patterns and best practices.