Skip to main content

Promise

A Promise is a special JavaScript object that represents the result of an asynchronous operation which may finish later, either successfully or with an error. Put simply, a Promise is a "promise" to deliver a result in the future: there is no answer right now, but there will be one when the operation completes.

Theory

TL;DR

  • A Promise represents the future result of an asynchronous operation.
  • States: pending, then either fulfilled or rejected; the transition happens only once.
  • resolve(value) moves it to fulfilled, reject(error) moves it to rejected.
  • Handling the result: .then(), .catch(), .finally(); each of them returns a new Promise.
  • Combinators: Promise.all(), Promise.allSettled(), Promise.race().
  • async/await is syntactic sugar over Promises, not a separate mechanism.

Quick example

javascript
function fetchData() { return new Promise((resolve) => { setTimeout(() => { resolve('Data received!'); }, 2000); }); } fetchData().then((data) => console.log(data));

fetchData() returns a Promise: after 2 seconds it resolves and then() receives the result.

The three states of a Promise

StateDescriptionExample
pendingwaiting, the operation has not finished yetthe request is in flight
fulfilledcompleted successfully, there is a resultthe data arrived
rejectedrejected, an error happenedthe server returned 404

Creating a Promise and handling the result

A Promise can be created by hand:

javascript
const promise = new Promise((resolve, reject) => { const success = Math.random() > 0.5; if (success) { resolve('Success!'); } else { reject('Error!'); } });
  • resolve(value) moves the Promise to the fulfilled state;
  • reject(error) moves it to the rejected state.

The result is handled with a chain of methods:

javascript
promise .then((result) => { console.log('Result:', result); }) .catch((error) => { console.error('Error:', error); }) .finally(() => { console.log('Always runs'); });
MethodWhen it is calledWhat it returns
.then()on success (resolve)a new Promise
.catch()on an error (reject)a new Promise
.finally()in any case (success or error)a new Promise

An example with an error:

javascript
function fetchUser() { return new Promise((_, reject) => { setTimeout(() => reject('User not found'), 1000); }); } fetchUser() .then(user => console.log(user)) .catch(err => console.error(err));

Output:

text
User not found

Promise chaining

Every .then() returns a new Promise, so asynchronous actions can be lined up into a chain:

javascript
getUser() .then(user => getPosts(user.id)) .then(posts => getComments(posts[0].id)) .then(comments => console.log(comments)) .catch(err => console.error('Error:', err));

The advantage: no nesting, the code reads top to bottom, unlike callback hell.

For a value that is already available there is a short form:

javascript
Promise.resolve('OK').then(console.log); // "OK" Promise.reject('Error').catch(console.error); // "Error"

Parallel Promises

Promise.all() waits until all of them settle successfully

javascript
Promise.all([ fetch('/user'), fetch('/posts'), ]).then(([userRes, postRes]) => { console.log('Both responses received'); });

If at least one Promise rejects, the whole all() becomes rejected.

Promise.allSettled() waits for all of them regardless of errors

javascript
Promise.allSettled([ fetch('/user'), fetch('/wrong-url'), ]).then(console.log);

The result:

javascript
[ { status: "fulfilled", value: Response }, { status: "rejected", reason: TypeError } ]

Promise.race() returns the first one that settles

javascript
Promise.race([ fetch('/slow'), fetch('/fast'), ]).then(console.log);

Promises and async/await

Promises are the foundation the async/await syntax is built on:

javascript
async function main() { try { const user = await getUser(); const posts = await getPosts(user.id); console.log(posts); } catch (err) { console.error(err); } }

Here await simply waits for a Promise, but it makes the code linear and clear.

A simple analogy:

A Promise is like a pizza delivery. You place the order and the Promise is created in the pending state. The courier is on the way, the operation is running. The courier arrives and the Promise resolves. The courier gets lost and the Promise rejects. You can subscribe to the result with then and handle errors with catch.

Summary

TermDescription
PromiseAn object representing the future result of an asynchronous operation
Statespending, then fulfilled or rejected
Methods.then(), .catch(), .finally()
Main benefitSimplifies asynchronous work and saves you from callback hell
Modern syntaxasync/await is built on Promises

Common mistakes

  • Thinking a Promise can be re-resolved: after the first resolve or reject the state is fixed forever and later calls are ignored.
  • Believing a Promise is lazy. The executor function starts immediately at new Promise(...), before any .then().
  • Forgetting return inside .then(), so the next link receives undefined instead of the data.
  • Putting .catch() somewhere other than the end of the chain: errors from the following .then() handlers are then left unhandled.
  • Reaching for Promise.all() where Promise.allSettled() is needed: one failure discards the whole result even though the other requests already succeeded.
  • Confusing Promise.race() with Promise.any(): race returns the first settled result even if it is an error, while any waits for the first successful one.
  • Wrapping a function that already returns a Promise into new Promise: that is an extra layer and a source of swallowed errors.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.