Promises in Depth: then, catch & Promise.all
Behind every async/await is a Promise. Learn to create them, chain .then/.catch, and run many at once — the real machinery of data-driven apps.
What you will learn
- Create a Promise and chain .then / .catch
- See how async/await maps onto Promises
- Run many Promises together with Promise.all
A Promise is a ticket for a future value
A Promise is an object that stands for a value you do not have yet — like a restaurant buzzer that will eventually light up. It lives in one of three states: pending (still waiting), fulfilled (the value arrived — we say it resolved), or rejected (something went wrong). You already used Promises through await; now we look at them directly.
You build one with new Promise(...). It hands you two functions: call resolve(value) when things succeed, or reject(error) when they fail. The example fakes a slow task that succeeds after a moment.
<p id="out">Working...</p>
<script>
function loadUser() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ name: "Asha", role: "Student" }); // success
// reject("User not found"); // failure path
}, 800);
});
}
loadUser().then(user => {
document.getElementById("out").textContent = "Loaded: " + user.name;
});
</script>Step by step:
loadUser()returns anew Promisethat starts pending — no value yet.- Inside,
setTimeoutwaits 800ms to simulate a slow server. - When the time is up,
resolve({ name: "Asha", ... })fulfils the Promise with that object. .then(user => ...)is the "when it resolves, do this" handler — it receives the resolved value asuser.- We show
user.name, so "Working..." becomes "Loaded: Asha".
Note: Output: At first: Working... After ~0.8s: Loaded: Asha
.then chains and .catch handles errors
The power of .then is that it chains: each .then returns a new Promise, so you can line up steps that depend on the previous one. A single .catch at the end catches a failure from any step in the chain.
<p id="r"></p>
<script>
function getNumber() {
return new Promise(resolve => resolve(5));
}
getNumber()
.then(n => n * 2) // 5 → 10 (return passes to the next .then)
.then(n => n + 1) // 10 → 11
.then(final => {
document.getElementById("r").textContent = "Result: " + final;
})
.catch(err => {
document.getElementById("r").textContent = "Failed: " + err;
});
</script>The value flows down the chain: getNumber() resolves with 5; the first .then returns 5 × 2 = 10, which becomes the input of the next .then (10 + 1 = 11), which the last .then displays. If any step threw an error, the chain would jump straight to .catch. The key habit: return a value from a .then so the next one receives it.
Note: Output: Result: 11
Tip: async/await is just a friendlier face on this. await loadUser() does the same job as loadUser().then(...) — and a try/catch around it does the same job as .catch. Knowing both means you can read any codebase.
Promise.all — run many at once
Often you need several independent results — say three API calls. Doing them one after another (await A, then await B, then await C) is slow, because each waits for the last. Promise.all runs them at the same time and waits for all to finish, which is far faster.
<p id="all">Loading three things...</p>
<script>
const wait = (label, ms) =>
new Promise(resolve => setTimeout(() => resolve(label), ms));
// Start all three together; wait for all to finish
Promise.all([
wait("A", 600),
wait("B", 400),
wait("C", 500)
]).then(results => {
document.getElementById("all").textContent = "All done: " + results.join(", ");
});
</script>Promise.all([...]) takes an array of Promises and starts them together. Because they overlap, the total wait is about the slowest one (600ms), not the sum (1500ms). When every Promise has resolved, .then runs once with an array of all the results, in the same order you listed them — so results is ["A", "B", "C"].
Note: Output: At first: Loading three things... After ~0.6s: All done: A, B, C
Watch out: Promise.all fails fast: if any one Promise rejects, the whole thing rejects immediately. When you instead want every result regardless of failures, use Promise.allSettled, which always waits for all and reports each as fulfilled or rejected.
The combinators at a glance
| Method | Resolves when | Use for |
|---|---|---|
Promise.all([...]) | ALL succeed (fails if any rejects) | Need every result, e.g. load a dashboard’s data |
Promise.allSettled([...]) | ALL finish (success or failure) | Want every outcome even if some fail |
Promise.race([...]) | The FIRST one settles | Timeouts, "whichever is fastest wins" |
Promise.any([...]) | The first SUCCESS | Try several sources, take the first that works |
Q. What is the main advantage of Promise.all over awaiting three Promises one by one?
✍️ Practice
- Create a Promise that resolves with a value after 1 second and read it with
.then. - Build a 3-step
.thenchain that transforms a number, and add a.catch. - Use
Promise.allto wait for two fake "fetches" and combine their results.
🏠 Homework
- Rewrite a small async/await function using
.then/.catchinstead, then convert it back — and note which you find clearer.