In short
A JavaScript promise is an object that stands for a value you will get later. This lesson shows how to create promises, use then and catch, and run many at once. What is a promise in JavaScript? A promise is like the buzzer a café gives you after you order.
A JavaScript promise is an object that stands for a value you will get later. This lesson shows how to create promises, use then and catch, and run many at once.
What is a promise in JavaScript?
A promise is like the buzzer a café gives you after you order. You do not have your coffee yet, but you hold something that promises to tell you when it is ready, or when something went wrong.
A promise is always in one of three states:
| State | Meaning | Café example |
|---|---|---|
| pending | still waiting | coffee is being made |
| fulfilled | finished with a value | buzzer rings, coffee is ready |
| rejected | failed with an error | "sorry, we are out of milk" |
Once a promise is fulfilled or rejected, it is settled and never changes again.
How to create a promise with new Promise
You create a promise with new Promise(). You give it a function with two parameters: resolve (call it on success) and reject (call it on failure).
function orderCoffee(type) {
return new Promise((resolve, reject) => {
console.log(`Making a ${type}...`);
setTimeout(() => {
if (type === "latte" || type === "espresso") {
resolve(`Your ${type} is ready`); // success
} else {
reject(new Error(`We don't make ${type}`)); // failure
}
}, 1000);
});
}
orderCoffee("latte")
.then((message) => console.log(message))
.catch((error) => console.log("Problem:", error.message))
.finally(() => console.log("Thank you for visiting"));
// Output:
// Making a latte...
// Your latte is ready (after 1 second)
// Thank you for visiting
.then()runs when the promise is fulfilled. It receives the value..catch()runs when the promise is rejected. It receives the error..finally()runs in both cases. It is good for clean-up, like hiding a loading spinner.
Try orderCoffee("tea") and you will see Problem: We don't make tea instead.
Tip: Always reject with an
Errorobject, likereject(new Error("...")). You get a clear message and a stack trace that shows where the problem started.
How to promisify setTimeout
"Promisify" means wrapping old callback code in a promise. A wait helper is the classic example, and you will use it often:
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
wait(1500).then(() => console.log("1.5 seconds have passed"));
// Output (after 1.5 seconds):
// 1.5 seconds have passed
setTimeout calls resolve when the time is up, so the promise becomes fulfilled.
How does promise chaining work?
Every .then() returns a new promise. Whatever you return from a .then() is passed to the next one. If you return a promise, the chain waits for it. This turns the callback pyramid into a flat list of steps.
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const getUser = (id) => wait(500).then(() => ({ id, name: "Maya" }));
const getOrders = (user) => wait(500).then(() => [{ id: 101, total: 49.99 }]);
let currentUser;
getUser(7)
.then((user) => {
currentUser = user;
return getOrders(user); // return the next promise
})
.then((orders) => {
console.log(`${currentUser.name} has ${orders.length} order(s)`);
})
.catch((error) => {
console.log("Something failed:", error.message); // one catch for all steps
});
// Output (after 1 second):
// Maya has 1 order(s)
Compare this to the callback hell example: the steps read from top to bottom, and a single .catch() handles an error from any step.
Common mistake: Forgetting
returninside.then(). Without it, the next.then()getsundefinedand does not wait for your inner promise.
How to run several promises at once
Sometimes tasks do not depend on each other. Then you can start them all together and wait for the results. JavaScript has four helpers. Each one takes an array of promises.
| Method | Fulfills when | Rejects when |
|---|---|---|
Promise.all | all succeed (array of values) | any one fails |
Promise.allSettled | all are finished (never rejects) | never |
Promise.race | the first one settles, success or failure | the first one to settle fails |
Promise.any | the first one succeeds | all of them fail |
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const fast = wait(300).then(() => "fast server");
const slow = wait(900).then(() => "slow server");
const broken = wait(500).then(() => { throw new Error("broken server"); });
Promise.all([fast, slow]).then((values) => console.log("all:", values));
Promise.allSettled([fast, broken]).then((results) => {
results.forEach((r) => console.log("allSettled:", r.status));
});
Promise.race([fast, slow]).then((winner) => console.log("race:", winner));
Promise.any([broken, slow]).then((first) => console.log("any:", first));
// Output:
// race: fast server
// allSettled: fulfilled
// allSettled: rejected
// all: [ "fast server", "slow server" ]
// any: slow server
Promise.allis perfect when you need every result, like loading a user and their settings together.Promise.allSettledgives you an object for each promise withstatusand eithervalueorreason. Use it when some tasks may fail and that is OK.Promise.raceis useful for timeouts: race your task against a timer.Promise.anyis useful when you have backup sources and only need one.
Practice exercise
Write a function rollDice() that returns a promise. After 1 second, it picks a random number from 1 to 6. If the number is 1, reject with new Error("You rolled a 1!"); otherwise resolve with the number. Call it with .then(), .catch() and .finally(). Then use Promise.all to roll three dice at once.
Hint: Math.floor(Math.random() * 6) + 1 gives a number from 1 to 6.
Key takeaways
- A promise represents a future value and is pending, fulfilled or rejected.
- Create one with
new Promise((resolve, reject) => { ... }). .then()handles success,.catch()handles errors,.finally()always runs.- Return values from
.then()to build a flat chain with one.catch(). - Use
Promise.all,allSettled,raceandanyto manage many promises.