JavaScriptMedium

How does async/await work in JavaScript?

01

The Short Answer

async/await is syntactic sugar over Promises that lets you write asynchronous code in a sequential, top-to-bottom style. Under the hood it still uses Promises, but the resulting code is significantly easier to read, write, and reason about.

02

How It Works

Before async/await, handling multiple dependent async operations meant chaining .then() calls. While functional, deeply nested chains became difficult to follow. async/await flattens this into code that reads like synchronous logic.

Consider fetching a user and then fetching their orders — two dependent async calls.

promises.tstypescript
function getUserOrders(userId: string) {
  return fetchUser(userId)
    .then(user => fetchOrders(user.id))
    .then(orders => console.log(orders))
    .catch(err => console.error(err));
}
async-await.tstypescript
async function getUserOrders(userId: string) {
  try {
    const user = await fetchUser(userId);
    const orders = await fetchOrders(user.id);
    console.log(orders);
  } catch (err) {
    console.error(err);
  }
}

Key insight

The async/await version reads top-to-bottom. Each await pauses only this function until the Promise resolves, then execution continues to the next line.

03

The async Keyword

Placing async before a function declaration does two things:

What async does

  • The function always returns a `Promise` — even if you return a plain value, it gets wrapped automatically
  • You can now use the `await` keyword inside the function body
04

The await Keyword

await pauses execution of the enclosing async function until the awaited Promise settles. It then unwraps the resolved value and returns it.

Important distinction

await only pauses the function it lives in — it does not block the main thread. Other code outside this function continues to execute while the function is suspended.

05

Error Handling

With raw Promises you chain .catch(). With async/await you use try...catch — the same pattern you already know from synchronous code. This makes error handling consistent and predictable.

error-handling.tstypescript
async function loadData() {
  try {
    const response = await fetch('/api/data');
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return await response.json();
  } catch (error) {
    console.error('Failed to load data:', error);
    throw error; // re-throw if caller needs to handle it
  }
}
06

Common Mistakes

🐌

Sequential awaits when parallel is possible

Using `await` in a loop when requests don't depend on each other forces them to run one-by-one. If each takes 1s and you have 5, that's 5 seconds total.

Use `Promise.all()` to run independent requests concurrently — same 5 requests finish in ~1 second.

🔄

Forgetting that async functions return Promises

Calling an `async` function without `await` or `.then()` means you get a Promise object, not the resolved value. This leads to subtle bugs where data appears as `[object Promise]`.

Always `await` or `.then()` the result of an async function when you need the resolved value.

parallel-vs-sequential.tstypescript
// ❌ Slow — sequential (5 seconds for 5 users)
for (const id of userIds) {
  const user = await fetchUser(id);
  results.push(user);
}

// ✅ Fast — parallel (≈1 second for 5 users)
const results = await Promise.all(
  userIds.map(id => fetchUser(id))
);
07

Why Interviewers Ask This

Nearly every frontend app fetches data from APIs. Interviewers use this question to gauge whether you understand JavaScript's async execution model, can handle errors gracefully, and know when to run operations sequentially vs in parallel. Demonstrating awareness of common pitfalls signals you can write reliable, performant async code in production.

Quick Revision Cheat Sheet

async: Makes a function return a Promise and enables `await` inside it

await: Pauses the `async` function until the Promise resolves, returns the value

Error handling: Use `try...catch` instead of `.catch()` chains

Parallel execution: Use `Promise.all()` for independent async operations

Does not block: `await` only pauses the enclosing function, not the main thread