TypeScript Async and Await
Async and await are keywords that make asynchronous code look and behave like synchronous code. Asynchronous operations — reading files, calling APIs, querying databases — take time. Instead of blocking the entire program while waiting, async code lets other work continue. The async and await keywords, combined with TypeScript's type system, make this code readable, type-safe, and easy to debug.
Synchronous vs Asynchronous
SYNCHRONOUS — each step waits for the one before: Step 1: Make tea → waits 5 minutes (nothing else happens) Step 2: Read news → starts only after tea is ready Step 3: Eat breakfast → starts only after reading ASYNCHRONOUS — tasks overlap: Step 1: Start boiling water → don't wait Step 2: Read news → while water boils Step 3: Water ready → make tea → continue breakfast Total time: much less than waiting sequentially
The async Keyword
Adding async before a function declaration makes that function return a Promise automatically. The return type becomes Promise<ReturnType>.
// Regular function
function getName(): string {
return "Priya";
}
// Async function — same logic, wrapped in a Promise
async function getNameAsync(): Promise<string> {
return "Priya"; // TypeScript wraps this in Promise<string> automatically
}
// Calling the async function
getNameAsync().then(name => console.log(name)); // Priya
The await Keyword
await pauses the function until the Promise resolves and returns the resolved value. It can only be used inside an async function.
FLOW:
async function doWork() {
console.log("Starting...");
let result = await someAsyncTask(); // pause here until done
console.log("Got result:", result); // resumes here after task completes
console.log("Done.");
}
Typing Async Functions
interface User {
id: number;
name: string;
email: string;
}
// Return type is always Promise<T> for async functions
async function fetchUser(id: number): Promise<User> {
// Simulating a real API call with a timer
await delay(1000); // wait 1 second
return {
id,
name: "Vikram",
email: "vikram@mail.com"
};
}
function delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function main(): Promise<void> {
let user = await fetchUser(1);
console.log(user.name); // Vikram (after 1 second)
console.log(user.email); // vikram@mail.com
}
main();
Error Handling with Async/Await
Wrap async code in try/catch to handle errors from awaited Promises. This works exactly like synchronous error handling.
async function loadProduct(id: number): Promise<void> {
try {
let response = await fetch(`https://api.example.com/products/${id}`);
if (!response.ok) {
throw new Error(`Server returned ${response.status}`);
}
let product: { name: string; price: number } = await response.json();
console.log(`Product: ${product.name}, Price: ₹${product.price}`);
} catch (error: unknown) {
if (error instanceof Error) {
console.log(`Failed to load product: ${error.message}`);
}
}
}
Multiple Awaits in Sequence
SEQUENCE:
Step 1 finishes → Step 2 starts → Step 3 starts
Total time = time(1) + time(2) + time(3)
async function processOrder(orderId: number): Promise<void> {
console.log("Starting order processing...");
let order = await getOrder(orderId); // wait for order
let user = await getUser(order.userId); // wait for user
let payment = await chargeCard(order.total); // wait for payment
console.log(`Order ${orderId} for ${user.name}: ${payment}`);
}
Parallel Execution with Promise.all
When tasks do not depend on each other, run them at the same time using Promise.all. This reduces total waiting time significantly.
PARALLEL:
Task 1 ─────────────────── done
Task 2 ──────── done
Task 3 ───────────────────────── done
Total time = time of the SLOWEST task
async function loadDashboardData(): Promise<void> {
try {
// Start all three requests at the same time
let [user, orders, notifications] = await Promise.all([
fetchUser(1),
fetchOrders(1),
fetchNotifications(1)
]);
console.log(`Welcome ${user.name}`);
console.log(`Orders: ${orders.length}`);
console.log(`Notifications: ${notifications.length}`);
} catch (error) {
console.log("One or more requests failed");
}
}
Promise.allSettled — Run All, Get All Results
// allSettled waits for all promises, even if some fail
async function tryMultipleSources(): Promise<void> {
let results = await Promise.allSettled([
fetchUser(1),
fetchUser(999), // this might fail (user not found)
fetchUser(2)
]);
for (let result of results) {
if (result.status === "fulfilled") {
console.log("Success:", result.value.name);
} else {
console.log("Failed:", result.reason);
}
}
}
Async with Interfaces and Generics
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
async function apiGet<T>(url: string): Promise<ApiResponse<T>> {
let response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
let data: T = await response.json();
return {
data,
status: response.status,
message: "OK"
};
}
interface Product {
id: number;
name: string;
price: number;
}
async function getProduct(): Promise<void> {
let result = await apiGet<Product>("https://api.example.com/products/1");
console.log(result.data.name); // TypeScript knows this is a Product
console.log(result.status); // 200
}
Async/Await Summary
| Concept | Syntax | Purpose |
|---|---|---|
| Async function | async function f(): Promise<T> | Marks a function as asynchronous |
| Await | let x = await promise | Waits for a promise and gets its value |
| Error handling | try / catch | Catches errors from awaited promises |
| Sequential calls | Multiple await statements | Run tasks one after another |
| Parallel calls | await Promise.all([...]) | Run tasks simultaneously |
| All results | await Promise.allSettled([...]) | Get all results even if some fail |
