Gyaan

Async/Await

intermediate async await promises error-handling

async/await is syntactic sugar on top of Promises. It lets us write asynchronous code that looks and reads like synchronous code. Under the hood, it’s still Promises — just with a much cleaner syntax.

The basics

An async function always returns a Promise. The await keyword pauses execution inside the async function until the Promise resolves, and then gives us the resolved value.

async function getUser() {
  const response = await fetch("/api/user"); // pauses here until fetch completes
  const user = await response.json();        // pauses here until parsing completes
  return user;                                // automatically wrapped in a Promise
}

// Calling it
getUser().then(user => console.log(user));

Without async/await, the same code with Promises would look like:

function getUser() {
  return fetch("/api/user")
    .then(response => response.json())
    .then(user => user);
}

Both do the exact same thing. But the async/await version reads top-to-bottom like normal code.

Error Handling with try/catch

Instead of .catch(), we use try/catch — just like synchronous error handling.

async function getUser() {
  try {
    const response = await fetch("/api/user");
    const user = await response.json();
    console.log(user);
  } catch (error) {
    console.log("Failed to fetch user:", error);
  } finally {
    console.log("Done"); // always runs
  }
}

This is much nicer than chaining .then().catch() everywhere, especially when we have multiple await calls.

Sequential vs Parallel Execution

This is a very important concept and a common interview question.

Sequential (slow) — one after another

async function loadData() {
  const users = await fetchUsers();     // waits 2 seconds
  const posts = await fetchPosts();     // waits 2 more seconds
  const comments = await fetchComments(); // waits 2 more seconds
  // Total: ~6 seconds (each one waits for the previous)
}

Parallel (fast) — all at once with Promise.all

If the requests don’t depend on each other, we should fire them all at once:

async function loadData() {
  const [users, posts, comments] = await Promise.all([
    fetchUsers(),     // starts immediately
    fetchPosts(),     // starts immediately
    fetchComments()   // starts immediately
  ]);
  // Total: ~2 seconds (all run in parallel, we wait for the slowest)
}

Common mistake: await in forEach

This is a trap that catches a lot of people. forEach does not wait for async callbacks. It fires them all off and moves on.

// WRONG — these all fire at once, forEach doesn't wait
const ids = [1, 2, 3];
ids.forEach(async (id) => {
  const user = await fetchUser(id);
  console.log(user); // order is NOT guaranteed
});
console.log("Done"); // this runs BEFORE any user is fetched!

If we need sequential processing, use a for...of loop:

// CORRECT — processes one at a time, in order
for (const id of ids) {
  const user = await fetchUser(id);
  console.log(user); // guaranteed order
}
console.log("Done"); // this runs AFTER all users are fetched

If we want parallel processing but still need to wait for all of them:

// CORRECT — parallel processing, wait for all
const users = await Promise.all(ids.map(id => fetchUser(id)));
console.log(users); // all users, in order

async/await with arrow functions

We can use async with arrow functions too:

const getUser = async (id) => {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
};

Key things to remember

  • async functions always return a Promise, even if we return a plain value
  • await can only be used inside an async function (or at the top level of a module)
  • await only pauses the current async function, not the entire program
  • Under the hood, everything after await goes to the Microtask Queue (just like .then())
  • For independent async operations, always use Promise.all() for better performance

In simple language, async/await lets us write async code that looks like regular synchronous code. It’s still Promises under the hood, but with a cleaner syntax. Use try/catch for errors, Promise.all for parallel operations, and never use await inside forEach.