← All concepts
Asynchronous JavaScript

Async/Await

await pauses an async function until a promise settles, without blocking the rest of the program.

Definition

The `async` keyword turns a function into one that always returns a promise; whatever it returns becomes the fulfillment value, and a thrown error becomes the rejection reason. Inside an async function, the `await` keyword can be placed in front of a promise, or any thenable, to pause that function's execution until the promise settles. Pausing only affects that one function's frame, not the whole program: control returns to whatever called the async function while the awaited promise is still pending, so other code keeps running normally. When the awaited promise settles, the async function's frame resumes exactly where it left off. If the promise fulfilled, await evaluates to the fulfillment value, and if it rejected, await throws that rejection reason, which a surrounding try/catch can catch. async/await is built on top of promises, it is syntax that lets promise-based code read like sequential, synchronous code, but it does not change the underlying scheduling: resuming an async function after an await still happens as a microtask, in the same event loop that runs .then() callbacks.

Examples

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

Each await pauses getUser's frame until the fetch and then the JSON parsing settle; the calling code was free to keep running while getUser was paused.

async function safeFetch(url) {
  try {
    const res = await fetch(url);
    return await res.json();
  } catch (err) {
    console.log("request failed:", err.message);
    return null;
  }
}

If the awaited fetch or json() call rejects, await throws that rejection inside safeFetch, and the try/catch handles it just like a synchronous error.

Common mistakes

  • Thinking await blocks the whole program; it only pauses the async function's own frame, control returns to the caller while the promise is still pending.
  • Awaiting a rejected promise without a try/catch, which lets the rejection propagate up as an unhandled error instead of being caught where you meant to handle it.
  • Awaiting several independent promises one after another instead of starting them together with Promise.all, which makes them wait on each other for no reason.

Key takeaways

  • async makes a function always return a promise, resolved with its return value or rejected with a thrown error.
  • await pauses only the async function's own execution until the promise settles, it does not block the rest of the program.
  • If the awaited promise rejects, await throws that reason, so a surrounding try/catch can handle it like a normal error.
  • Resuming after an await is scheduled as a microtask, the same timing promises use.
Check your understanding

What happens when you await a promise that rejects, inside an async function with no try/catch around it?

Where you see this

  • Writing sequential-looking code for a series of dependent API calls instead of nesting .then() callbacks.
  • Wrapping awaited calls in try/catch to handle network or parsing failures in one place.
  • Awaiting Promise.all([...]) to run independent requests concurrently while still writing linear code.
  • Pausing a function until a database query or file read resolves without blocking the rest of the app.

Practice this

Further reading

Previous
Promises
Next
Microtasks vs. Macrotasks