← All concepts
Asynchronous JavaScript

Promises

A promise is a placeholder for a value that starts pending and settles exactly once, to fulfilled or rejected.

Definition

A `Promise` represents the eventual result of an asynchronous operation. It starts in the pending state and settles exactly one time, either to fulfilled, meaning the operation succeeded and produced a value, or rejected, meaning the operation failed and produced a reason. Once a promise settles, its state and value are locked in forever: calling `.then()` on an already-fulfilled promise, or `.catch()` on an already-rejected one, still runs the handler, just as if the settlement had just happened. `.then(onFulfilled, onRejected)` registers callbacks for the two outcomes, `.catch(onRejected)` is shorthand for `.then(undefined, onRejected)`, and `.finally(onSettled)` runs regardless of which way the promise settled, without receiving the value or reason. Each of these methods returns a new promise, which is what makes chaining possible: whatever a handler returns becomes the fulfillment value of that new promise, and a thrown error inside a handler becomes its rejection reason. Handlers registered with `.then`, `.catch`, and `.finally` never run synchronously, even if the promise is already settled when you attach them; they are always scheduled as microtasks, which is why a `.then()` callback runs after all currently running synchronous code, but before any setTimeout callback.

Examples

const p = new Promise((resolve, reject) => {
  const ok = Math.random() > 0.5;
  if (ok) resolve("done");
  else reject(new Error("failed"));
});
p.then((value) => console.log("fulfilled:", value))
 .catch((err) => console.log("rejected:", err.message));

The executor settles the promise once, to fulfilled or rejected. Only the matching handler runs, .then's callback for a resolve, .catch's callback for a reject, never both.

Promise.resolve(1)
  .then((n) => n + 1)
  .then((n) => n * 10)
  .then((n) => console.log(n)); // 20

Each .then returns a new promise fulfilled with whatever the callback returns, so the value flows through the chain from one handler to the next.

Common mistakes

  • Expecting a promise to change state after it settles; once it is fulfilled or rejected, that outcome and value are locked in forever.
  • Forgetting that .then, .catch, and .finally always run as microtasks, even if the promise already settled before you attached them, so the handler never runs synchronously.
  • Returning a value from a .then callback and expecting the chain to skip ahead; the next .then only runs once that returned value (or promise) actually resolves.

Key takeaways

  • A promise starts pending and settles exactly once, to fulfilled or rejected, never both and never again after that.
  • .then handles fulfillment, .catch handles rejection, and .finally runs either way without seeing the value or reason.
  • Each .then/.catch/.finally returns a new promise, which is what makes chaining work.
  • Promise callbacks are always scheduled as microtasks, never run synchronously.
Check your understanding

A promise is fulfilled with the value 5. If you call .then(cb) on it a second later, what happens?

Where you see this

  • Wrapping a fetch() call so callers can .then() the parsed response or .catch() a network failure.
  • Chaining dependent asynchronous steps, like reading a file then parsing then validating, without nesting callbacks.
  • Using .finally() to hide a loading spinner regardless of whether a request succeeded or failed.
  • Combining several independent promises with Promise.all or Promise.allSettled.

Practice this

Further reading

Previous
The Event Loop
Next
Async/Await