A Promise is a JavaScript object that represents the eventual result of an asynchronous operation. Instead of passing callbacks into a function, a promise lets you attach handlers to the future success or failure of that operation. This makes async code much easier to read and reason about.
A Promise is always in one of three states:
Once a promise is fulfilled or rejected it is settled and its state never changes again.
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)) // Operation succeeded!
.catch(error => console.error(error)); // only runs on rejection
Each .then() returns a new promise, which allows you to chain multiple async steps in a readable sequence. The value returned from one .then() is passed as the argument to the next.
fetch('https://api.example.com/user/1')
.then(response => response.json()) // parse JSON
.then(user => {
console.log(user.name);
return fetch(`/api/posts?userId=${user.id}`);
})
.then(response => response.json())
.then(posts => console.log(posts))
.catch(error => console.error('Error:', error))
.finally(() => console.log('Done')); // always runs
Promise.all() takes an array of promises and returns a single promise that resolves when all of them resolve, or rejects as soon as any one of them rejects. Use it when tasks are independent and can run simultaneously.
const p1 = fetch('/api/users').then(r => r.json());
const p2 = fetch('/api/posts').then(r => r.json());
const p3 = fetch('/api/comments').then(r => r.json());
Promise.all([p1, p2, p3])
.then(([users, posts, comments]) => {
console.log(users, posts, comments);
})
.catch(err => console.error('One failed:', err));
JavaScript provides several other static methods for handling multiple promises:
const slow = new Promise(res => setTimeout(() => res('slow'), 2000));
const fast = new Promise(res => setTimeout(() => res('fast'), 500));
const fail = Promise.reject(new Error('failed'));
// allSettled "” never rejects
Promise.allSettled([slow, fast, fail]).then(results => {
results.forEach(r => console.log(r.status, r.value ?? r.reason));
});
// race "” first to settle wins
Promise.race([slow, fast]).then(v => console.log(v)); // 'fast'
// any "” first to FULFILL wins
Promise.any([fail, fast]).then(v => console.log(v)); // 'fast'
Always attach a .catch() at the end of a promise chain to handle any rejection that bubbles up. The .finally() handler runs regardless of success or failure "” useful for cleanup like hiding a loading spinner.
function loadUser(id) {
return fetch(`/api/users/${id}`)
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
});
}
loadUser(42)
.then(user => console.log('Loaded:', user.name))
.catch(err => console.error('Failed:', err.message))
.finally(() => console.log('Request complete'));
Explore 500+ free tutorials across 20+ languages and frameworks.