Promises & Async/Await
The problem
Coordinating multiple asynchronous operations with raw callbacks quickly becomes an unreadable, deeply nested mess ('callback hell'), and there's no clean way to express 'wait for this, then do that' or to propagate an error from deep inside a chain of async steps.
Why now
The event loop explained that async results arrive via the callback queue, but not how to compose them cleanly. Promises are a higher-order abstraction over that raw callback mechanism — and higher-order-functions already established the value of naming and reusing a pattern instead of hand-writing it every time.
Mental model
A promise is a placeholder for a value that doesn't exist yet, in one of three states: pending, fulfilled, or rejected — once it settles, it stays settled forever. async/await is syntax that makes code reading like synchronous code while it's secretly still using the event loop underneath: await pauses the async function (not the whole runtime) until the promise settles, then resumes with the result or throws the error.
Requires
- The Event LoopJavaScript Runtime
Promises are built entirely on the event loop's microtask queue; you need the loop's stack-then-queue model already in place to understand why .then() callbacks run when they do.
- Higher-Order FunctionsProgramming Fundamentals
A promise's .then/.catch take callback functions as arguments, and chaining promises is function composition; the higher-order-function pattern is the direct scaffolding promises are built from.
Unlocks
Used in
- fetch() and virtually every asynchronous browser and Node.js API
- async/await syntax in modern JavaScript, now the default way to write asynchronous code
- Promise.all/allSettled for coordinating multiple concurrent async operations
- Error handling in async code via try/catch around await, replacing nested callback error checks
Projects
- Rewrite a callback-based function (e.g. a fake delayed API call) using Promises, then again using async/await, comparing readability
- Use Promise.all to fetch three resources concurrently and measure the time savings versus awaiting them one at a time
Examples
- const data = await fetch(url).then(r => r.json()); reads top-to-bottom despite being fully asynchronous underneath
- Promise.all([a, b, c]) runs three async operations concurrently and resolves once all three finish, rather than one after another
Resources
- MDN — Using promisesarticle
- MDN — async functiondocs
Mastery checklist
- I can explain the three states of a Promise and why a settled promise can't change state again
- I can rewrite nested callbacks as a Promise chain and then as async/await
- I can use Promise.all to run independent async operations concurrently instead of sequentially
- I can handle errors in async/await code using try/catch