On this page
Promises
What is a promise?
A promise (Promise) is an object that represents the future result of an asynchronous operation. It can be in one of three states:
- Pending — The operation has not finished yet
- Fulfilled — The operation completed successfully (has a value)
- Rejected — The operation failed (has an error)
Once a promise is resolved or rejected, its state is immutable: it cannot change.
Creating promises
The new Promise() constructor takes a function with two callbacks:
const promise = new Promise((resolve, reject) => {
// async operation...
if (success) {
resolve(result); // transition to fulfilled
} else {
reject(new Error('something failed')); // transition to rejected
}
});Consuming promises
.then()
Receives the value when the promise resolves. It returns a new promise, allowing chaining:
promise
.then(value => transform(value))
.then(transformed => useIt(transformed));.catch()
Catches any error in the chain:
promise
.then(value => processIt(value))
.catch(error => handleError(error));.finally()
Executes always, regardless of whether the promise was resolved or rejected. It is useful for cleaning up resources (closing spinners, releasing locks, etc.).
Chaining promises
Each .then() returns a new promise. If you return a value, the next promise resolves with that value. If you return another promise, the chain waits for it to resolve:
getUser(1)
.then(user => getProfile(user.id))
.then(profile => getPhotos(profile.albumId))
.then(photos => showGallery(photos))
.catch(error => showError(error));Promise combinators
JavaScript offers four static methods to work with multiple promises simultaneously:
| Method | Resolves when... | Rejects when... |
|---|---|---|
Promise.all |
All resolve | One fails |
Promise.allSettled |
All finish (success or failure) | Never |
Promise.race |
The first finishes | The first fails |
Promise.any |
The first succeeds | All fail |
Pattern: timeout with Promise.race
A common pattern is to race a request against a timer to implement timeouts:
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
);
return Promise.race([promise, timeout]);
}Common mistakes
- Forgetting to return in a
.then()— The next promise receivesundefined - Not handling errors — Always add
.catch()at the end of the chain - Creating unnecessary promises — If you already have a promise, don't wrap it in another one
Practice
- Create and consume a promise: Write a function
wait(ms)that returns a promise that resolves aftermsmilliseconds. Consume it with.then()to print a message when it resolves. - Chain promises with error handling: Create two functions that return promises:
getUser(id)andgetPosts(userId). Chain them with.then()and add.catch()and.finally()to handle errors and cleanup. - Use Promise.all and Promise.allSettled: Create 3 promises (one that intentionally fails) and run them with
Promise.alland then withPromise.allSettled. Compare the results and observe how each combinator handles the failure.
In the next lesson we will learn async/await, the modern way to work with promises.
// Creating a promise
function findUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, name: 'Carlos', email: '[email protected]' });
} else {
reject(new Error('Invalid ID'));
}
}, 1000);
});
}
// Consuming with .then/.catch/.finally
findUser(1)
.then(user => {
console.log('User:', user.name);
return user.email;
})
.then(email => {
console.log('Email:', email);
})
.catch(error => {
console.error('Error:', error.message);
})
.finally(() => {
console.log('Search completed');
});
// Chaining promises (each .then returns a new promise)
function getPosts(userId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve([
{ id: 1, title: 'First post' },
{ id: 2, title: 'Second post' },
]);
}, 500);
});
}
findUser(1)
.then(user => getPosts(user.id))
.then(posts => console.log('Posts:', posts))
.catch(error => console.error(error));
// Simulating async requests with promises
function getUsers() {
return new Promise((resolve) => {
setTimeout(() => resolve([{ id: 1, name: 'Ana' }]), 1000);
});
}
function getPosts() {
return new Promise((resolve) => {
setTimeout(() => resolve([{ id: 1, title: 'Hello' }]), 1500);
});
}
function getComments() {
return new Promise((resolve) => {
setTimeout(() => resolve([{ id: 1, text: 'Great!' }]), 800);
});
}
// Promise.all - waits for ALL (fails if one fails)
Promise.all([getUsers(), getPosts(), getComments()])
.then(([users, posts, comments]) => {
console.log('All data:', users, posts, comments);
})
.catch(error => {
console.error('A request failed:', error);
});
// Promise.allSettled - waits for all (never fails)
Promise.allSettled([getUsers(), getPosts(), getComments()])
.then(results => {
results.forEach(r => {
if (r.status === 'fulfilled') {
console.log('OK:', r.value);
} else {
console.log('Error:', r.reason);
}
});
});
// Promise.race - returns the first to resolve/reject
const timeout = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Timeout')), 5000);
});
Promise.race([getUsers(), timeout])
.then(data => console.log('Data:', data))
.catch(err => console.error(err.message));
// Promise.any - returns the first to RESOLVE
Promise.any([getUsers(), getPosts(), getComments()])
.then(first => console.log('First successful:', first));
Sign in to track your progress