Preparation
Read this introduction before the session. It explains the concepts we'll practice during the session.
The Problem: Callback Hell
Last week you wrote code like this:
validateOrder(order, (err, validation) => {
if (err) return console.error(err);
calculateTotal(order, (err, total) => {
if (err) return console.error(err);
checkStock(order, (err, stock) => {
if (err) return console.error(err);
processPayment(order, total, (err, receipt) => {
if (err) return console.error(err);
// Finally done... but look at this pyramid!
});
});
});
});This is called callback hell - deeply nested callbacks that are hard to read and maintain. Each new step adds another level of indentation. Error handling is repetitive. The code grows sideways instead of downward.
Promises solve this.
What is a Promise?
A Promise is an object representing an operation that hasn't completed yet. Think of it like an order ticket at a cafe:
You place an order and receive a ticket (the Promise)
The ticket promises you'll get coffee eventually
Two outcomes: you get your coffee (resolved) or they're out (rejected)
📚 Glossary: Promise An object representing the eventual completion (or failure) of an asynchronous operation. A Promise is always in one of three states: pending, fulfilled (resolved), or rejected.
Consuming Promises: .then() and .catch()
When you have a Promise, you can attach callbacks using .then() and .catch():
.then()runs when the Promise resolves (succeeds).catch()runs when the Promise rejects (fails)
📚 Glossary: resolve / reject A Promise "resolves" when the operation succeeds, passing the result to
.then(). It "rejects" when the operation fails, passing the error to.catch().
Chaining Promises
The magic of Promises: .then() returns a new Promise, so you can chain them:
No more nesting! The code flows downward, and one .catch() handles all errors.
📚 Glossary: then / catch
.then(callback)schedules a callback to run when a Promise resolves..catch(callback)schedules a callback to run when a Promise rejects. Both return new Promises, enabling chaining.
Creating Promises
You can create your own Promises using new Promise():
The Promise constructor takes a function with two parameters:
resolve- call this when the operation succeedsreject- call this when the operation fails
async/await: Even Cleaner Syntax
async/await is syntactic sugar over Promises - it makes async code look synchronous:
asyncbefore a function makes it return a Promiseawaitpauses execution until a Promise resolvesThe code reads top-to-bottom, like synchronous code
📚 Glossary: async / await
asyncdeclares a function that returns a Promise.awaitpauses execution until a Promise settles, then returns its resolved value. Only works insideasyncfunctions.
Error Handling with try/catch
With async/await, you handle errors using try/catch:
This is the same pattern as synchronous error handling - familiar and readable.
Promise.all: Parallel Operations
Sometimes you want to run multiple operations at once:
Promise.all() takes an array of Promises and returns a Promise that resolves when ALL of them resolve.
📚 Glossary: Promise.all Takes an array of Promises and returns a single Promise that resolves when all input Promises resolve (with an array of results), or rejects when any input Promise rejects.
Summary
Promise
Object representing future completion/failure
resolve
Promise succeeded, pass result to .then()
reject
Promise failed, pass error to .catch()
.then()
Handle successful result
.catch()
Handle error
async
Makes function return a Promise
await
Pause until Promise resolves
Promise.all
Run multiple Promises in parallel
Pre-Reading (Optional)
If you want to go deeper, read these MDN pages:
The Tea Shop API
During the session you'll work with a real API:
Example:
Last updated