Promises in JavaScript
Representing a future value with pending, fulfilled, and rejected states.
2 min read
A lot of what JavaScript does takes time — fetching data over the network, reading a file, waiting on a timer — and none of that can happen instantly while the rest of the page stays responsive. A Promise is an object representing a value that isn't available yet, but will be (or will fail to be) at some point.
The three states
A promise starts pending, and settles exactly once into either:
- fulfilled — the operation succeeded, and the promise has a resulting value.
- rejected — the operation failed, and the promise has a reason (usually an error).
const promise = new Promise((resolve, reject) => {
const success = true;
if (success) {
resolve("It worked!");
} else {
reject(new Error("It failed."));
}
});resolve and reject are functions the executor calls to settle the promise — you'll write code like this rarely in practice (most promises come from built-in APIs like fetch), but seeing it makes clear that a promise is just an object wrapping an eventual result.
Consuming a promise: then and catch
promise
.then(result => console.log(result)) // runs if fulfilled
.catch(error => console.error(error)); // runs if rejected.then() registers a callback for the success case; .catch() registers one for failure. Neither runs synchronously — the callback fires only once the promise actually settles, whenever that happens.
Why not just use a callback?
Before promises, asynchronous code used callbacks directly, which get unwieldy fast once one async step depends on another — deeply nested callbacks that are hard to read and hard to handle errors in consistently, sometimes called "callback hell." Promises fix this by making async operations chainable:
fetchUser(id)
.then(user => fetchPosts(user.id))
.then(posts => console.log(posts))
.catch(error => console.error("Something failed:", error));Each .then() returns a new promise, so the chain reads top to bottom as a sequence of steps, and a single .catch() at the end handles a failure from any step in the chain — you don't need a separate error handler at every level.
Promise.all — running things in parallel
Promise.all([fetchUser(1), fetchUser(2), fetchUser(3)])
.then(users => console.log(users)); // array of all three results, in orderPromise.all waits for every promise in the array to fulfill, then resolves with all their results together — and rejects immediately if any one of them rejects. Use it when several independent async operations can run at the same time instead of one after another.
Promises are the foundation everything else in this section builds on. The next lesson covers async/await — syntax that lets you write promise-based code that reads like ordinary, synchronous code.