Introduction to Asynchronous Programming in TypeScript
Modern web development is unthinkable without asynchronous programming. Whether it's API requests, working with the file system, or timers — all these operations require non-blocking code execution. TypeScript, as a superset of JavaScript, provides powerful tools for working with asynchrony, adding strict typing and improved code readability.
In this guide, we will thoroughly examine all aspects of asynchronous programming in TypeScript: from basic Promise to advanced patterns with async/await and parallel task execution.
1. Basics of Promise in TypeScript
What is a Promise?
A Promise is an object representing the result of an asynchronous operation. It can be in one of three states:
- Pending — initial state, the operation has not completed yet.
- Fulfilled — the operation completed successfully.
- Rejected — the operation completed with an error.
Creating a Promise with Typing
TypeScript allows you to strictly type the return values of a Promise. Let's look at an example:
// Function returning a Promise with a data typefunction fetchUserData(userId: number): Promise<User> { return new Promise<User>((resolve, reject) => { setTimeout(() => { const user: User = { id: userId, name: 'Ivan Ivanov', email: 'ivan@example.com' }; if (userId > 0) { resolve(user); } else { reject(new Error('Invalid user ID')); } }, 1000); });}
// Interface for data typinginterface User { id: number; name: string; email: string;}
// Using the PromisefetchUserData(1) .then((user: User) => { console.log(`User: ${user.name}`); }) .catch((error: Error) => { console.error(`Error: ${error.message}`); });Promise Chaining
Promises can be chained together for sequential execution of asynchronous operations:
function getUserPosts(userId: number): Promise<string[]> { return new Promise((resolve) => { setTimeout(() => resolve(['Post 1', 'Post 2', 'Post 3']), 500); });}
function getPostComments(postTitle: string): Promise<string[]> { return new Promise((resolve) => { setTimeout(() => resolve(['Comment 1', 'Comment 2']), 300); });}
// Promise ChainfetchUserData(1) .then((user) => { console.log(`User found: ${user.name}`); return getUserPosts(user.id); }) .then((posts) => { console.log(`Posts found: ${posts.length}`); return getPostComments(posts[0]); }) .then((comments) => { console.log(`Comments on the first post: ${comments.length}`); }) .catch((error) => { console.error('An error occurred:', error); });2. Async/Await — A Modern Approach
Async/Await Syntax
The async and await keywords make asynchronous code look synchronous, which significantly improves its readability. A function marked with async always returns a Promise.
// Async function with typed return valueasync function loadUserProfile(userId: number): Promise<UserProfile> { try { const user = await fetchUserData(userId); const posts = await getUserPosts(userId); return { user, posts, lastLogin: new Date() };