TS TypeScript

Async Patterns

Lesson 5 of 5 ~8 min

Modern JavaScript and TypeScript rely heavily on asynchronous operations — fetching data, reading files, waiting for user input. Understanding how to handle these operations cleanly is essential.

The evolution of async code

JavaScript's approach to async code has evolved over time, each pattern solving the problems of the last:

// 1. Callbacks (old pattern — avoid for new code)
fetchData(function(result) {
  processData(result, function(result2) {
    saveData(result2, function(result3) {
      // Callback hell!
    });
  });
});

// 2. Promises (better)
fetchData()
  .then(result => processData(result))
  .then(result2 => saveData(result2))
  .catch(err => console.error(err));

// 3. Async/await (modern — cleanest)
async function handleData() {
  try {
    const result = await fetchData();
    const result2 = await processData(result);
    await saveData(result2);
  } catch (err) {
    console.error(err);
  }
}

Promises

A Promise represents a value that may not be available yet. It can be in one of three states: pending, fulfilled, or rejected.

// Creating a promise
function delay(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// Using a promise
delay(1000).then(() => console.log("Done after 1 second"));

Async/await

The async keyword marks a function as asynchronous. The await keyword pauses execution until a Promise resolves:

// async function — always returns a Promise
async function getUser(id: number): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  const user: User = await response.json();
  return user;
}

// Using the function
const user = await getUser(1);
console.log(user.name);

Error handling with try/catch

async function fetchData() {
  try {
    const response = await fetch("https://api.example.com/data");

    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }

    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error("Failed to fetch:", error);
  } finally {
    console.log("Request complete");
  }
}
Editor
TypeScript
Console

        

Key points

You can only use await inside an async function (or at the top level of ES modules). The await keyword pauses execution of the function until the Promise resolves, without blocking the rest of your program.

Quiz

What does the await keyword do?
Challenge

Write an async function called countdown that logs "3", "2", "1" with a 1-second delay between each, then logs "Go!". Use async/await and setTimeout with a Promise.