What is a Promise, and how does it compare to callbacks and async/await?

JavaScript Async Intermediate 1–2 Yrs2–3 Yrs3–5 Yrs Conceptual 1 min read

Quick Answer

A Promise is an object representing a value that will be available later — it's pending, then fulfilled or rejected. It replaces nested callbacks with .then() chaining and centralised .catch(). async/await is syntax sugar over promises that lets you write asynchronous code that reads synchronously.

A Promise represents a future value — it starts pending and ends up either fulfilled or rejected. It solved callback hell: instead of nesting callbacks you chain .then calls and put one .catch at the end for errors. async/await is built on promises and just makes the code read top-to-bottom — await pauses until the promise settles, and you handle errors with a normal try/catch. The combinators matter too: Promise.all to run independent things in parallel and fail fast, allSettled when I want every result regardless of failures.

  • Callbacks — pass a function to call when done; nesting several leads to “callback hell” and error handling is repeated everywhere.
  • Promises — flatten the nesting with .then, one .catch at the end handles any failure in the chain. A promise settles once and is immutable after.
  • async/await — const user = await getUser(id) reads like sequential code; errors use normal try/catch. Under the hood it's still promises.
  • Combinators: Promise.all (all succeed, or reject on first failure), Promise.allSettled (wait for all, get each result), Promise.race (first to settle), Promise.any (first to fulfil).
  • await in a loop serialises; map to promises and Promise.all to parallelise independent work.

JavaScript — parallel vs serial
// serial — 3 round trips one after another
for (const id of ids) results.push(await fetchUser(id));

// parallel — all at once
const results = await Promise.all(ids.map(id => fetchUser(id)));