Back to JavaScript
JavaScript
medium
mid

What is a Promise in JavaScript, how do you resolve one, and what are common use cases?

A Promise is an object representing the eventual result of an async operation. States: pending → fulfilled or rejected (once, then immutable). Create with `new Promise((resolve, reject) => …)`. Use for: fetch, timers, file I/O, any async API — and chain with `.then` / `.catch` or await. Promises moved JS off callback hell.

4 min read·~8 min to think through

What it is

A Promise is a placeholder for a value that arrives later. Its three states:

ts
pending → fulfilled (with a value)
rejected  (with a reason)

Once settled, it never changes. You read its outcome via callbacks (.then / .catch / .finally) or with await.

Creating one

js
const wait = (ms) => new Promise((resolve, reject) => {
  if (ms < 0) reject(new Error("invalid"));
  else setTimeout(() => resolve("done"), ms);
});

The executor function runs synchronously and gets resolve and reject. Call one to settle the Promise.

Static helpers

  • Promise.resolve(value) — already fulfilled.
  • Promise.reject(reason) — already rejected.
  • Promise.all(arr) — all-or-nothing aggregator.
  • Promise.allSettled(arr) — collect all outcomes.
  • Promise.race(arr) — first to settle wins.
  • Promise.any(arr) — first to fulfill wins.

Consuming

js
wait(100)
  .then((v) => console.log(v))
  .catch((e) => console.error(e))
  .finally(() => console.log("cleanup"));

Or with await:

js
try {
  const v = await wait(100);
  console.log(v);
} catch (e) {
  console.error(e);
} finally {
  console.log("cleanup");
}

Use cases

  • HTTP requests (fetch).
  • File I/O (fs.promises).
  • Database queries.
  • Timers (new Promise(r => setTimeout(r, ms))).
  • User-driven async (animation frame, requestIdleCallback, ResizeObserver-driven measurement).
  • Aggregating multiple async operations.

Why they replaced callbacks

js
// Callback hell
getUser(id, (err, user) => {
  if (err) return cb(err);
  getPosts(user.id, (err, posts) => {
    if (err) return cb(err);
    render(posts);
  });
});

// Promises
getUser(id).then(getPosts).then(render).catch(handleErr);

// async/await
const user = await getUser(id);
const posts = await getPosts(user.id);
render(posts);

Flatter code, single error path, composable.

Microtask vs macrotask

.then callbacks run on the microtask queue — before the next event loop tick's macrotasks (setTimeout). Means a chain of .thens runs before any I/O resolves.

Anti-patterns

  • The "promise constructor anti-pattern" — wrapping an existing Promise in new Promise. Just return it.
  • Forgetting to return from a .then — breaks the chain.
  • Mixing callbacks and Promises in the same code path.
  • Unhandled rejection (no .catch, no try/catch around await).

Interview framing

"A Promise is an object with three states: pending, fulfilled, rejected — settled exactly once and then immutable. Create with new Promise((resolve, reject) => ...); consume with .then / .catch / .finally or await. They replaced callback hell with composable async — same single error path, flatter code. Static helpers (Promise.all / allSettled / race / any) give you the aggregation primitives. .then callbacks run on the microtask queue, ahead of macrotasks. Avoid the constructor anti-pattern of wrapping a Promise in new Promise."

Follow-up questions

  • What's the microtask queue?
  • Compare callbacks, promises, async/await.
  • What's the constructor anti-pattern?

Common mistakes

  • Constructor anti-pattern.
  • Forgetting to return inside .then.
  • No .catch / try/catch.

Performance considerations

  • Microtask queue runs before next macrotask; long chains can starve I/O if you spin.

Edge cases

  • Resolving with another Promise — it chains.
  • Synchronous throw inside executor → reject.
  • Multiple resolve calls — only the first counts.

Real-world examples

  • Every fetch call, every db.query, every setTimeout wrapped, every modern web API that's async.

Senior engineer discussion

Seniors prefer async/await for control flow and Promise.all/allSettled for parallel work; they recognize and reject anti-patterns.

Related questions