Promises give asynchronous code a cleaner, chainable structure than nested callbacks. This lesson covers creating, chaining, and combining promises, the foundation for the async/await syntax covered next.
What Is a Promise?
A promise is an object representing a value that isn't available yet but will be at some point, either successfully ("resolved") or unsuccessfully ("rejected"). Instead of passing a callback into a function, that function returns a promise, and you attach .then() for success and .catch() for failure.
Promises can be chained: each .then() returns a new promise, letting you express a sequence of async steps without nesting, flattening what would otherwise be callback hell into a readable, linear chain.
.finally() runs regardless of outcome, useful for cleanup like hiding a loading state.
Promise.all() waits for every promise to resolve, rejecting immediately if any one rejects.
Promises Cheatsheet
The Promise API methods you'll use in almost every async Node.js codebase.
Method
Behavior
Promise.resolve(value)
Creates an already-resolved promise
Promise.reject(error)
Creates an already-rejected promise
Promise.all([...])
Resolves when all resolve, rejects on first rejection
Promise.allSettled([...])
Waits for all, regardless of success or failure
Promise.race([...])
Settles as soon as the first promise settles
Promise.any([...])
Resolves as soon as any promise resolves
Creating a Promise Manually
Most of the time you'll consume promises returned by other functions (fetch, fs.promises.readFile, database drivers). Occasionally you need to wrap a callback-based API yourself using the Promise constructor.
function wait(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
wait(1000).then(() => console.log('1 second passed'));
Running Promises in Parallel
Promise.all() is one of the most useful combinators: it lets you kick off several independent async operations at once and wait for all of them together, instead of awaiting them one after another and wasting time.
This runs all three requests concurrently. If they were awaited sequentially instead, the total time would be the sum of all three, not the maximum.
Common Mistakes
Forgetting a .catch() (or try/catch with async/await), letting rejections become unhandled promise rejections.
Awaiting promises sequentially with separate await statements when they could safely run in parallel with Promise.all().
Using Promise.all() when one failure shouldn't cancel the others, Promise.allSettled() is usually the better fit there.
Returning a promise from inside .then() without returning it (forgetting the return keyword), breaking the chain.
Key Takeaways
A promise represents a value that will be available later, either resolved or rejected.
.then()/.catch()/.finally() let you chain async steps without nested callbacks.
Promise.all() runs promises in parallel and waits for all to resolve.
Promise.allSettled() waits for every promise regardless of individual failures.
Pro Tip
Reach for Promise.all() any time you're about to await multiple independent operations one after another. If they don't depend on each other's results, running them concurrently is almost always faster with no downside.
You now understand promises deeply. Next, learn async/await, the syntax that makes promise-based code read like synchronous code.