Promises
A promise is a placeholder for a value that starts pending and settles exactly once, to fulfilled or rejected.
Loading visual…
Definition
A `Promise` represents the eventual result of an asynchronous operation. It starts in the pending state and settles exactly one time, either to fulfilled, meaning the operation succeeded and produced a value, or rejected, meaning the operation failed and produced a reason. Once a promise settles, its state and value are locked in forever: calling `.then()` on an already-fulfilled promise, or `.catch()` on an already-rejected one, still runs the handler, just as if the settlement had just happened. `.then(onFulfilled, onRejected)` registers callbacks for the two outcomes, `.catch(onRejected)` is shorthand for `.then(undefined, onRejected)`, and `.finally(onSettled)` runs regardless of which way the promise settled, without receiving the value or reason. Each of these methods returns a new promise, which is what makes chaining possible: whatever a handler returns becomes the fulfillment value of that new promise, and a thrown error inside a handler becomes its rejection reason. Handlers registered with `.then`, `.catch`, and `.finally` never run synchronously, even if the promise is already settled when you attach them; they are always scheduled as microtasks, which is why a `.then()` callback runs after all currently running synchronous code, but before any setTimeout callback.
Examples
const p = new Promise((resolve, reject) => {
const ok = Math.random() > 0.5;
if (ok) resolve("done");
else reject(new Error("failed"));
});
p.then((value) => console.log("fulfilled:", value))
.catch((err) => console.log("rejected:", err.message));The executor settles the promise once, to fulfilled or rejected. Only the matching handler runs, .then's callback for a resolve, .catch's callback for a reject, never both.
Promise.resolve(1)
.then((n) => n + 1)
.then((n) => n * 10)
.then((n) => console.log(n)); // 20Each .then returns a new promise fulfilled with whatever the callback returns, so the value flows through the chain from one handler to the next.
Common mistakes
- Expecting a promise to change state after it settles; once it is fulfilled or rejected, that outcome and value are locked in forever.
- Forgetting that .then, .catch, and .finally always run as microtasks, even if the promise already settled before you attached them, so the handler never runs synchronously.
- Returning a value from a .then callback and expecting the chain to skip ahead; the next .then only runs once that returned value (or promise) actually resolves.
Key takeaways
- A promise starts pending and settles exactly once, to fulfilled or rejected, never both and never again after that.
- .then handles fulfillment, .catch handles rejection, and .finally runs either way without seeing the value or reason.
- Each .then/.catch/.finally returns a new promise, which is what makes chaining work.
- Promise callbacks are always scheduled as microtasks, never run synchronously.
A promise is fulfilled with the value 5. If you call .then(cb) on it a second later, what happens?
Where you see this
- Wrapping a fetch() call so callers can .then() the parsed response or .catch() a network failure.
- Chaining dependent asynchronous steps, like reading a file then parsing then validating, without nesting callbacks.
- Using .finally() to hide a loading spinner regardless of whether a request succeeded or failed.
- Combining several independent promises with Promise.all or Promise.allSettled.