Promises
Learn how JavaScript Promises represent the eventual result of an asynchronous operation, and how to use .then(), .catch(), Promise.all(), Promise.race(), and Promise.allSettled().
Reading Time
20 min
Lesson
Lesson 30 of 48
What Is a Promise?
A Promise is an object representing the eventual result of an asynchronous operation — something that hasn't finished yet, but will finish (successfully or not) at some point in the future. Instead of forcing your code to freeze and wait, a Promise lets you keep going and attach instructions for what to do once the result is ready. This solves the exact problem from the previous lesson: it gives asynchronous work a proper, catchable way to report success or failure.
The Three States of a Promise
| State | Meaning |
|---|---|
| Pending | The initial state — the operation has not completed yet |
| Fulfilled | The operation completed successfully, and has a resulting value |
| Rejected | The operation failed, and has a reason (typically an Error) |
Creating a Promise
You create a Promise with the Promise constructor, which takes a single function called the executor. The executor receives two functions as arguments — resolve and reject — call resolve(value) when the operation succeeds, or reject(error) when it fails. Once a promise settles (resolves or rejects), its state is locked in permanently; it can never change again.
The Promise Constructor
Console Output
Click “Run” to see the console output here.
Consuming a Promise: then, catch, finally
.then() registers callbacks to run when a promise fulfills (first argument) or rejects (second, optional argument). .catch() is shorthand for handling only the rejection case, and reads more cleanly when placed after one or more .then() calls. .finally() runs once the promise settles, regardless of whether it fulfilled or rejected — just like the finally block from try/catch, it's the place for cleanup that must always happen.
then / catch / finally
Console Output
Click “Run” to see the console output here.
Chaining Promises
Each call to .then() returns a brand new promise, which is what makes chaining possible. If you return a plain value from inside a .then() callback, that value becomes the fulfillment value of the next promise in the chain. If you return another promise, the chain waits for that promise to settle before continuing — this is how you sequence multiple asynchronous steps, one after another, without nesting callbacks inside callbacks.
Chaining Multiple Steps
Console Output
Click “Run” to see the console output here.
Forgetting to Return Inside a .then()
A very common bug: forgetting to return the promise (or value) you're producing inside a .then() callback. Without the return, the next .then() in the chain runs immediately with undefined instead of waiting for your inner operation to finish — the chain silently stops tracking the work you actually care about.
The Missing return Bug
Console Output
Click “Run” to see the console output here.
Promise.all() — Run Many, Wait for All
Promise.all() takes an array of promises and returns a single promise that fulfills with an array of all their results, in the same order, once every one of them has fulfilled. If even one of the promises rejects, Promise.all() immediately rejects with that error, without waiting for the others. It's the right tool when you need several independent operations to all succeed before moving on.
Promise.all() Example
Console Output
Click “Run” to see the console output here.
Promise.race() — First One Wins
Promise.race() also takes an array of promises, but settles as soon as the first one settles — whether that first one fulfills or rejects. This is useful for scenarios like timeouts, where you want to race a real operation against a promise that rejects after a set duration, so a slow request doesn't hang forever.
Promise.race() Example
Console Output
Click “Run” to see the console output here.
Promise.allSettled() — Wait for Everything, No Matter What
Unlike Promise.all(), Promise.allSettled() never short-circuits on a rejection. It waits for every promise to settle and returns an array of result objects, each shaped like { status: 'fulfilled', value } or { status: 'rejected', reason }. Reach for this when you want to know the outcome of every operation — successes and failures alike — instead of stopping at the first failure.
Promise.allSettled() Example
Console Output
Click “Run” to see the console output here.
Choosing the Right Combinator
Use Promise.all() when every operation must succeed and you'll bail out if any fail. Use Promise.allSettled() when you want the outcome of every operation regardless of individual failures. Use Promise.race() when you only care about whichever promise settles first, such as implementing a timeout.