Skip to main content

Promises from First Principles

intermediate15 min readLesson 72 of 143

States, chaining, error propagation, and finally โ€” how promises replace callback pyramids and compose.

A promise is a value that may not exist yet. It has three states: pending โ†’ fulfilled (with a value) or rejected (with an error) โ€” and once settled, never changes again.

Chaining

Every .then returns a new promise, which is what makes chains flat instead of nested:

fetchJSON("/api/user")
  .then((user) => fetchJSON("/api/orders/" + user.id))
  .then((orders) => render(orders))
  .catch((err) => showError(err));   // catches ANY step above
  .finally(() => hideSpinner());     // runs either way

Three rules worth memorizing:

  • Returning a value in .then fulfills the next promise with that value.
  • Returning a promise from .then makes the chain wait for it.
  • A thrown error (or a rejected promise returned) skips forward to the nearest .catch โ€” errors propagate like try/catch, through the whole chain.

Errors are values too

.catch(onRejected) is .then(undefined, onRejected). A .catch that returns normally recovers the chain โ€” later .thens run. That is how fallback logic works:

getFromCache(id)
  .catch(() => getFromNetwork(id)) // fallback
  .then(render);

finally

.finally(fn) runs on both outcomes and receives nothing โ€” it is for cleanup (spinner off, lock released), not for the result.

Now practice

Promises โ€” PracticeChain with fallbacks: a resilient value pipeline, sequential dependency chains, and error recovery with finally semantics.3 challenges ยท ยท ~18 min