Introduction
A Promise is an object representing the eventual completion or failure of an asynchronous operation. Promises were introduced to solve callback hell by providing a chainable, more predictable structure for async code. Every Promise exists in one of three states: pending (initial state, operation not yet complete), fulfilled (operation completed successfully, with a resulting value), or rejected (operation failed, with a reason/error). Once a Promise settles (fulfilled or rejected), its state and value are immutable.
Cricket analogy: A Promise is like a DRS review request: it's pending while the third umpire checks replays, fulfilled if the decision confirms the appeal with a result, or rejected if overturned with a reason, and once the decision is given it never changes again.
Syntax
const promise = new Promise((resolve, reject) => {
const success = true;
if (success) {
resolve('Operation succeeded');
} else {
reject(new Error('Operation failed'));
}
});
promise
.then((result) => console.log(result))
.catch((error) => console.error(error))
.finally(() => console.log('Done'));Explanation
The Promise constructor takes an executor function with resolve and reject parameters. Calling resolve(value) transitions the Promise from pending to fulfilled and passes value to any .then() handlers. Calling reject(reason) transitions it to rejected, triggering .catch() handlers. The .then() method returns a new Promise, enabling chaining: each .then() callback's return value (or thrown error) determines the next Promise's state. .finally() runs regardless of outcome, useful for cleanup like closing a database connection.
Cricket analogy: resolve(value) is like the third umpire signaling 'out' and passing the replay evidence to the commentary team (.then() handlers), while reject(reason) signals 'not out' with the reasoning; .finally() is like the ground staff resetting the pitch cover regardless of the verdict.
Example
function fetchUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) resolve({ id, name: 'Alice' });
else reject(new Error('Invalid id'));
}, 500);
});
}
function fetchOrders(userId) {
return new Promise((resolve) => {
setTimeout(() => resolve(['order1', 'order2']), 500);
});
}
fetchUser(42)
.then((user) => {
console.log('User:', user.name);
return fetchOrders(user.id);
})
.then((orders) => console.log('Orders:', orders))
.catch((err) => console.error('Error:', err.message));
// Combining multiple independent promises
Promise.all([fetchUser(1), fetchUser(2)])
.then((users) => console.log('All users:', users));Output
After ~500ms: 'User: Alice' logs, then after another ~500ms 'Orders: [ 'order1', 'order2' ]' logs, because returning a Promise from a .then() callback chains it — the next .then() waits for it to settle. If fetchUser rejected (e.g., id <= 0), the chain would skip directly to .catch(), printing 'Error: Invalid id'. Promise.all resolves once all input promises fulfill, or rejects immediately if any one rejects.
Cricket analogy: This chained sequence is like a broadcast delaying the player-profile graphic by 500ms after confirming the batsman's name, then another 500ms before showing the strike-rate stats, and if the player ID was invalid, it skips straight to an 'unknown player' error graphic instead.
Key Takeaways
- A Promise has exactly one of three states: pending, fulfilled, or rejected, and settles only once.
- resolve() fulfills a Promise; reject() rejects it, both are immutable after settling.
- Returning a value or Promise inside .then() chains cleanly instead of nesting callbacks.
- A thrown error or rejected Promise anywhere in the chain is caught by the nearest .catch().
- Promise.all, Promise.race, Promise.allSettled, and Promise.any coordinate multiple Promises differently.
Practice what you learned
1. Which of the following are the three possible states of a Promise?
2. What happens when you return a value from inside a .then() callback?
3. If a Promise in the middle of a .then() chain rejects, what happens?
4. What does Promise.all([p1, p2, p3]) do?
Was this page helpful?
You May Also Like
Callbacks in Node.js
How Node.js uses callback functions to handle asynchronous operations without blocking the main thread.
Async/Await in Node.js
Writing asynchronous code that reads like synchronous code using the async/await syntax built on Promises.
The Error-First Callback Pattern
The Node.js convention of passing an error as the first callback argument for consistent async error handling.
Node.js Architecture and the Event Loop
Understand Node.js's single-threaded, non-blocking architecture and how the event loop phases process callbacks.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics