JavaScript Promises

Handle async code with promises, then, catch, async/await, and Promise combinators like all.

TL;DR

  1. 01Promises represent values that arrive later from async work.
  2. 02Chain handlers with then, catch, and finally methods.
  3. 03Use async and await for cleaner, synchronous-looking async code.

Tips

  1. 01Use <code>Promise.all()</code> for independent async work so every request runs in parallel instead of waiting one at a time.
  2. 02Use <code>Promise.allSettled()</code> when you need the outcome of every promise, including failures, instead of stopping at the first rejection.

Warnings

  1. 01A single rejected promise inside <code>Promise.all()</code> rejects the whole call, so use <code>Promise.allSettled()</code> when partial failure is acceptable.
  2. 02Forgetting to <code>return</code> a promise inside <code>.then()</code> breaks the chain, since the next handler runs before that nested work finishes.

Creating Promises

  • Every promise locks into one of three states for life, and you control the transition yourself with new Promise and an executor that calls resolve or reject.
    const p = new Promise((resolve, reject) => {
      if (ok) resolve(value);
      else reject(error);
    });
  • Use Promise.resolve() to wrap any value in an already-fulfilled promise.
    Promise.resolve(42).then(v => console.log(v));
  • Use Promise.reject() to create an already-rejected promise for testing.
    Promise.reject('Error!').catch(e => console.log(e));
  • Promises have three states: pending, fulfilled, or rejected, and never change back.
  • Most APIs you use, like fetch, already return promises for you to consume.

Handling Results

  • A promise is useless until something reacts to it — use .then() to run code when the promise fulfills with a value.
    promise.then(result => console.log(result));
  • Use .catch() to handle any error in the promise or in a .then callback.
    promise.catch(error => console.error(error));
  • Use .finally() to run cleanup code regardless of success or failure.
    promise.finally(() => console.log('Done'));
  • Each .then() returns a new promise, which lets you chain steps together.
  • Always include a .catch() or try/catch — unhandled rejections cause warnings.

Promise Chaining

  • What turns a single async call into a readable pipeline? Each .then() returns a new promise carrying the return value of its callback.
    fetch('/api/users/1')
      .then(res => res.json())
      .then(user => console.log(user.name))
      .catch(err => console.error(err));
  • A .catch() anywhere in a chain catches errors from all preceding steps.
  • Return a new promise inside .then() to wait for it before the next step.
    getUser(id)
      .then(user => getOrders(user.id))
      .then(orders => console.log(orders));
  • A .catch() followed by .then() lets the chain recover and continue.
    fetch('/api/data')
      .catch(() => getCachedData())
      .then(data => render(data));
  • Avoid nesting .then() inside another .then() — flatten the chain instead to avoid promise hell.

Combining Promises

  • Four combinators, four different failure behaviors — pick wrong and one slow request can stall your whole UI. Use Promise.all() to wait for all promises and get an array of results.
    const [a, b] = await Promise.all([p1, p2]);
  • Use Promise.race() to settle as soon as the first promise finishes.
    Promise.race([fast, slow]).then(r => console.log(r));
  • Use Promise.any() to get the first fulfilled value, ignoring rejections.
    Promise.any([p1, p2]).then(r => console.log(r));
  • Use Promise.allSettled() when you want every result, success or failure.
    Promise.allSettled([p1, p2]).then(results => console.log(results));
  • Promise.all() rejects fast if any promise fails, so prefer allSettled for bulk work.

Common Patterns

  • Sequential awaits are the most common performance bug in async code — run promises one after another using await on each call only when each step truly depends on the last.
    const a = await first();
    const b = await second();
  • Run promises in parallel with Promise.all for faster completion.
    const [a, b] = await Promise.all([first(), second()]);
  • Wrap callbacks like setTimeout in a promise for use with await.
    const delay = ms => new Promise(r => setTimeout(r, ms));
  • Use fetch to make HTTP requests, then await response.json() for parsing.
    const res = await fetch('/api/users/1');
    const user = await res.json();
  • Choose sequential when steps depend on each other and parallel when they do not.

FAQ