Promises
A promise is an object representing a value that isn't available yet — the eventual result of an asynchronous operation. Instead of passing callbacks in, an async function hands back a promise, and you attach what should happen next with .then(), .catch() and .finally().
Promise states
A promise is always in one of three states:
- pending — the operation hasn't finished yet;
- fulfilled — it succeeded, with a value;
- rejected — it failed, with a reason (usually an
Error).
Once fulfilled or rejected, a promise is settled and never changes again.
Creating a promise
Most of the time you'll use promises returned by APIs like fetch. To wrap a callback-based or timer-based operation yourself, use the Promise constructor:
function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } function rollDie() { return new Promise((resolve, reject) => { setTimeout(() => { const value = 4; if (value < 1) reject(new Error("Bad roll")); else resolve(value); }, 20); }); } wait(10).then(() => console.log("waited")); rollDie().then(n => console.log("rolled", n));
waited rolled 4
The function passed to new Promise (the executor) runs immediately and receives two functions: call resolve(value) on success or reject(error) on failure.
then, catch, finally
function divide(a, b) { return new Promise((resolve, reject) => { if (b === 0) reject(new Error("Division by zero")); else resolve(a / b); }); } divide(10, 4) .then(result => console.log("result:", result)) .catch(err => console.log("error:", err.message)) .finally(() => console.log("first done")); divide(1, 0) .then(result => console.log("result:", result)) .catch(err => console.log("error:", err.message));
result: 2.5 error: Division by zero first done
then handles success, catch handles failure, and finally runs either way — ideal for hiding a loading spinner.
Chaining
Every .then() returns a new promise, resolved with whatever its callback returns. If the callback returns another promise, the chain waits for it. This turns nested callbacks into a flat sequence:
const wait = (ms, value) => new Promise(r => setTimeout(() => r(value), ms)); wait(10, 2) .then(n => n * 10) .then(n => wait(10, n + 1)) .then(n => { console.log("final:", n); throw new Error("something broke"); }) .then(() => console.log("skipped")) .catch(err => console.log("caught:", err.message)) .then(() => console.log("chain continues after catch"));
final: 21 caught: something broke chain continues after catch
An error anywhere — a rejection or a thrown exception — skips the following thens and jumps to the next catch. A single catch at the end handles errors from every step.
return inside a then callback. Without it, the next step runs immediately with undefined instead of waiting for the inner promise.Running promises together
const wait = (ms, value, fail) => new Promise((res, rej) => setTimeout(() => (fail ? rej(new Error(value)) : res(value)), ms)); Promise.all([wait(30, "a"), wait(10, "b"), wait(20, "c")]) .then(values => console.log("all:", values)); Promise.allSettled([wait(10, "ok"), wait(20, "nope", true)]) .then(results => console.log("allSettled:", results.map(r => r.status))); Promise.race([wait(30, "slow"), wait(10, "fast")]) .then(v => console.log("race:", v)); Promise.any([wait(10, "x", true), wait(20, "y")]) .then(v => console.log("any:", v));
race: fast allSettled: [ 'fulfilled', 'rejected' ] any: y all: [ 'a', 'b', 'c' ]
| Method | Fulfils when… | Rejects when… |
|---|---|---|
Promise.all | All fulfil (values in original order) | Any one rejects |
Promise.allSettled | All settle — never rejects | — |
Promise.race | The first to settle fulfils | The first to settle rejects |
Promise.any | The first to fulfil | All reject |
Use all for independent requests that are all needed, allSettled when some may fail and you want every result, race for timeouts, and any for "whichever mirror answers first".
Ready-made promises
Promise.resolve(value) and Promise.reject(error) create already-settled promises — useful for returning cached values from a function that normally returns a promise. Promise.withResolvers() returns a promise together with its resolve and reject functions, for cases where they're needed outside the executor.
Unhandled rejections
A rejected promise with no catch produces an "Uncaught (in promise)" error in the browser console; in Node.js it can crash the process. Always end a promise chain with .catch(), or await it inside try/catch.