A Promise in JavaScript is an object that represents a value that is not available yet but will be at some point, such as the result of a network request. Instead of blocking your program while you wait, a promise lets you attach code to run once the value arrives, or once it fails. It is the foundation of asynchronous JavaScript, and the modern async/await syntax you see everywhere in 2026 is built directly on top of it. Understanding promises makes that syntax click and makes debugging async bugs far less mysterious.
What a Promise actually is
When you start an operation that takes time, like fetching data, JavaScript does not wait around. It hands you a promise immediately, an object that is a placeholder for the eventual result. The actual value fills in later.
A promise is always in one of three states. It starts as pending. It then either becomes fulfilled, carrying a result value, or rejected, carrying an error. Once it settles into fulfilled or rejected, it never changes again. The callbacks you attach run later, scheduled by the event loop.
| State |
Meaning |
| Pending |
The work is still in progress |
| Fulfilled |
The work succeeded and has a value |
| Rejected |
The work failed and has an error |
How you handle the result
Because the value is not ready when you get the promise, you register callbacks to run when it settles. then handles success, and catch handles failure.
-- Fetch data, then handle success or failure
fetch("https://api.example.com/data")
.then(function (response) { return response.json(); })
.then(function (data) { console.log(data); })
.catch(function (error) { console.error(error); });
Each then returns a new promise, which is why you can chain them. The value returned from one then becomes the input to the next. A single catch at the end will handle a rejection from anywhere earlier in the chain.
Promises vs async await
The same logic can be written with async/await, which is syntactic sugar over promises. It does not replace them; it makes them read like ordinary sequential code.
| Style |
Strength |
Trade-off |
| then / catch |
Explicit chaining, good for streams of steps |
Can nest and grow hard to read |
| async / await |
Reads top to bottom, easy try/catch |
Still promises underneath; needs an async function |
-- The same fetch using async await
async function load() {
try {
const response = await fetch("https://api.example.com/data");
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
Most modern code uses async/await for everyday work and reaches for raw promise methods when combining several promises at once.
How to use promises well
- Always handle rejection. Attach a
catch, or wrap await in try/catch. An unhandled rejection can crash or silently fail.
- Return values inside then. What you return becomes the next link in the chain; forgetting to return breaks the flow.
- Run independent work in parallel. Use a combinator like
Promise.all when several operations do not depend on each other.
- Prefer async await for sequences. It is easier to read and debug than long
then chains.
Common mistakes
- Forgetting to return inside then. The chain receives
undefined and the next step misbehaves.
- Deeply nested then calls. Nesting recreates the callback pyramid promises were meant to fix. Flatten it or use
await.
- Swallowing errors. An empty
catch hides real failures. Log or rethrow so problems surface.
- Awaiting in a loop unnecessarily. Waiting for each item in turn can be slow when they could run together.
FAQ
Is a Promise the same as async await?
No. A promise is the underlying object representing a future value. async/await is friendlier syntax for working with promises; the promises are still there beneath it.
What happens to a rejected promise with no catch?
It becomes an unhandled rejection. Depending on the environment it may print a warning or crash the process. Always handle rejections.
Can a promise change state twice?
No. Once a promise settles as fulfilled or rejected, it is locked. Later attempts to resolve or reject it are ignored.
When should I use Promise.all?
When you have several independent promises and want them to run at the same time, then continue once all have completed. If any one rejects, the whole thing rejects.
Where to go next
See what async await is in 2026, what a callback is in 2026, and how to debug JavaScript in 2026.