Asynchronous Programming in TypeScript: Complete Guide

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Asynchronous Programming in TypeScript: A Complete Beginner's Guide

\n\n

Asynchronous programming is one of the key concepts in modern web development. If you are just starting to learn TypeScript, understanding how to work with asynchronous code will open up opportunities for creating fast and responsive applications. In this guide, we will cover the basics of asynchronous programming in TypeScript: from simple callbacks to modern async/await, with an emphasis on the strict typing that TypeScript provides.

\n\n

What is asynchronous programming and why is it needed?

\n\n

In synchronous programming, code executes sequentially: each subsequent operation waits for the previous one to complete. This can lead to the interface "freezing" if, for example, data needs to be loaded from a server. Asynchronous programming allows you to run long-running operations (API requests, file reading, timers) in the background without blocking the main execution thread. TypeScript, being a superset of JavaScript, fully supports asynchronous patterns and adds a powerful type system to them.

\n\n

Promise: The Foundation of Asynchrony in TypeScript

\n\n

Promise is an object representing the result of an asynchronous operation, which can be in one of three states: pending, fulfilled, or rejected. In TypeScript, we can specify the type of data the promise will return, making the code more predictable.

\n\n

Example of creating a Promise with typing:

\n\n
// Function returning a Promise with type string\nfunction fetchUserData(userId: number): Promise<string> {\n    return new Promise((resolve, reject) => {\n        setTimeout(() => {\n            if (userId > 0) {\n                resolve(`User data for ID ${userId}`);\n            } else {\n                reject(new Error("Invalid user ID"));\n            }\n        }, 1000);\n    });\n}\n\n// Using the Promise\nfetchUserData(123)\n    .then((data: string) => {\n        console.log(data); // "User data for ID 123"\n    })\n    .catch((error: Error) => {\n        console.error(error.message);\n    });
\n\n

Note: we explicitly specified that the promise returns string, and in .then() the data parameter also has the type string. This helps avoid errors at compile time.

\n\n

Promise Chaining and Error Handling

\n\n

Promises can be chained, passing the result of one asynchronous operation to another. TypeScript will automatically infer types at each step.

\n\n

Example of a Promise chain:

\n\n
interface User {\n    id: number;\n    name: string;\n}\n\nfunction getUser(id: number): Promise<User> {\n    return new Promise((resolve) => {\n        setTimeout(() => resolve({ id, name: "Anna" }), 500);\n    });\n}\n\nfunction getPosts(userId: number): Promise<string[]> {\n    return new Promise((resolve) => {\n        setTimeout(() => resolve(["Post 1", "Post 2"]), 500);\n    });\n}\n\n// Call chain\ngetUser(1)\n    .then((user: User) => {\n        console.log(`User: ${user.name}`);\n        return getPosts(user.id); // Return a new Promise\n    })\n    .then((posts: string[]) => {\n        console.log(`Posts: ${posts.join(", ")}`);\n    })\n    .catch((error: Error) => {\n        console.error("Error:", error.message);\n    });
\n\n

If an error occurs at any stage, execution will jump to the nearest .catch(). This centralized error handling is one of the main advantages of promises over callbacks.

\n\n

Async/await: Elegant Asynchronous Syntax

\n\n

The async/await syntax makes asynchronous code look like synchronous code, which simplifies reading it

Blogs

Book Recommendations