Frontend Labs
⋯

13. Callbacks and Promises: what actually happens under the hood

Before Promises, async results were delivered via callbacks — functions passed in and invoked later. Nesting them for sequential async steps produces "callback hell":

getUser(id, (err, user) => {
  if (err) return handleError(err);
  getPosts(user.id, (err, posts) => {
    if (err) return handleError(err);
    getComments(posts[0].id, (err, comments) => {
      if (err) return handleError(err);
      render(user, posts, comments);   // 3 levels deep, error handling repeated 3x
    });
  });
});

A Promise is a state machine with exactly three states — pending → fulfilled or pending → rejected — and once settled, it never changes again. .then() doesn't run the callback synchronously even if the promise is already resolved; it always schedules it as a microtask, which is what guarantees consistent ordering.

function delay(ms, value) {
  return new Promise((resolve) => setTimeout(() => resolve(value), ms));
}

delay(100, "done")
  .then((value) => { console.log(value); return value.toUpperCase(); })
  .then((upper) => console.log(upper))
  .catch((err) => console.error("caught:", err));   // catches a rejection at ANY step above

Promise combinators for concurrent work:

Promise.all([p1, p2, p3]);        // resolves when ALL resolve; rejects fast on the FIRST rejection
Promise.allSettled([p1, p2, p3]); // always resolves, with { status, value|reason } for each
Promise.race([p1, p2, p3]);       // settles as soon as the FIRST one settles (resolve or reject)
Promise.any([p1, p2, p3]);        // resolves on the FIRST fulfillment; rejects only if ALL reject

War story. A team used Promise.all to fetch 5 independent dashboard widgets' data. One flaky endpoint failing 2% of the time took down the entire dashboard on every request, because Promise.all rejects as soon as any one promise rejects — the other 4 successful results were thrown away. Switching to Promise.allSettled and rendering each widget independently based on its own settled result fixed it in one line.

Generated from real-life-JS-guide.md · 8 Oct 2026 16:36