One of the most frequently asked JavaScript interview questions is:

What is a Promise and why do we need it?

Interviewers often ask:

  • What problems do Promises solve?
  • What are Promise states?
  • How does Promise chaining work?
  • How does Promise error handling work?
  • What is the difference between Promise.all() and Promise.allSettled()?
  • How do Promises interact with the Event Loop?
  • How is async/await related to Promises?

Let's understand Promises from first principles.

1. Why Do We Need Promises?#

Before Promises, asynchronous JavaScript was commonly written using callbacks.

Example:

JAVASCRIPT
getUser(function (user) {
  getPosts(user.id, function (posts) {
    getComments(posts[0].id, function (comments) {
      console.log(comments);
    });
  });
});

This creates:

Callback Hell

Problems:

  • Deep nesting
  • Difficult error handling
  • Harder debugging
  • Poor readability
  • Difficult composition

Promises were introduced to make asynchronous code more manageable.

2. What is a Promise & Its States#

A Promise is an object that represents the eventual completion or failure of an asynchronous operation.

Think of a Promise as:

A Placeholder
For A Future Value

Example:

JAVASCRIPT
const promise = fetch("/users");

At this moment:

Data Is Not Available Yet

The Promise represents the future result.

Real-Life Analogy#

Imagine ordering food online.

Order Placed
      ↓
Food Being Prepared
      ↓
Delivered

A Promise works similarly:

Pending
   ↓
Fulfilled
or
Rejected

Promise States#

Every Promise exists in one of three states.

Pending

Operation Still Running

Fulfilled

Operation Completed Successfully

Rejected

Operation Failed

Visual Representation#

         Pending
        /       \
       /         \
Fulfilled      Rejected

A Promise can change state only once.

3. Creating a Promise#

Example:

JAVASCRIPT
const promise = new Promise((resolve, reject) => {
  const success = true;

  if (success) {
    resolve("Success");
  } else {
    reject("Failed");
  }
});

Resolve#

JAVASCRIPT
resolve(value);

Marks the Promise as:

Fulfilled

Reject#

JAVASCRIPT
reject(error);

Marks the Promise as:

Rejected

4. Consuming a Promise & Error Handling#

JAVASCRIPT
promise.then((result) => {
  console.log(result);
});

Output:

Success

Handling Errors#

JAVASCRIPT
promise.catch((error) => {
  console.error(error);
});

Output:

Failed

Complete Example#

JAVASCRIPT
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Data Loaded");
  }, 1000);
});

promise.then((data) => {
  console.log(data);
});

Output after 1 second:

Data Loaded

5. Promise Chaining & Flow Visualization#

One of the biggest advantages of Promises.

Example:

JAVASCRIPT
fetchUser()
  .then((user) => {
    return fetchPosts(user.id);
  })
  .then((posts) => {
    return fetchComments(posts[0].id);
  })
  .then((comments) => {
    console.log(comments);
  });

Instead of:

Nested Callbacks

we get:

Linear Flow

How Chaining Works#

Every:

JAVASCRIPT
.then()

returns a new Promise.

Example:

JAVASCRIPT
Promise.resolve(5)
  .then((value) => {
    return value * 2;
  })
  .then((value) => {
    console.log(value);
  });

Output:

10

Promise Chain Visualization#

Promise
   ↓
then()
   ↓
then()
   ↓
then()

Each step receives the previous result.

Error Handling in Chains#

Example:

JAVASCRIPT
fetchUser()
  .then((user) => {
    throw new Error("Failed");
  })
  .catch((error) => {
    console.log(error.message);
  });

Output:

Failed

Why This Is Powerful#

A single:

JAVASCRIPT
.catch()

can handle errors from the entire chain.

Example:

JAVASCRIPT
task1().then(task2).then(task3).catch(handleError);

Much cleaner than nested callbacks.

6. Execution Order & Event Loop Priority#

What is the output?

JAVASCRIPT
console.log("Start");

const promise = new Promise((resolve) => {
  console.log("Inside");

  resolve();
});

console.log("End");

Answer#

Output:

Start
Inside
End

Important:

Promise Constructor Executes Immediately

Many developers incorrectly assume it is asynchronous.

Event Loop Interview Question#

What is the output?

JAVASCRIPT
console.log("1");

Promise.resolve().then(() => {
  console.log("2");
});

console.log("3");

Answer#

Output:

1
3
2

Why?

JAVASCRIPT
.then()

creates a:

Microtask

Microtasks execute after synchronous code completes.

Promise vs setTimeout#

What is the output?

JAVASCRIPT
console.log("1");

setTimeout(() => {
  console.log("2");
}, 0);

Promise.resolve().then(() => {
  console.log("3");
});

console.log("4");

Answer#

Output:

1
4
3
2

Why?

Execution order:

Sync Code
 ↓
Microtasks
 ↓
Macrotasks

Promises use:

Microtask Queue

Timers use:

Macrotask Queue

7. Parallel Promises: Promise.all vs. allSettled vs. race vs. any#

Runs multiple Promises concurrently.

Example:

JAVASCRIPT
Promise.all([fetchUsers(), fetchPosts(), fetchComments()]);

Behavior:

Wait For All

Example#

JAVASCRIPT
Promise.all([Promise.resolve("A"), Promise.resolve("B")]).then((result) => {
  console.log(result);
});

Output:

["A", "B"]

Promise.all Failure Behavior#

JAVASCRIPT
Promise.all([Promise.resolve("A"), Promise.reject("Error")]);

Output:

Rejected

One failure rejects the entire Promise.

Promise.allSettled()#

Waits for all Promises regardless of success or failure.

Example:

JAVASCRIPT
Promise.allSettled([Promise.resolve("A"), Promise.reject("Error")]);

Output:

JAVASCRIPT
[
  {
    status: "fulfilled",
    value: "A",
  },
  {
    status: "rejected",
    reason: "Error",
  },
];

Promise.race()#

Returns the first settled Promise.

Example:

JAVASCRIPT
Promise.race([fetchUsers(), timeout()]);

Useful for request timeouts.

Promise.any()#

Returns the first fulfilled Promise.

Example:

JAVASCRIPT
Promise.any([api1(), api2(), api3()]);

Success from any source wins.

Promise.finally()#

Runs regardless of success or failure.

Example:

JAVASCRIPT
fetchData()
  .then(handleSuccess)
  .catch(handleError)
  .finally(() => {
    hideLoader();
  });

Useful for cleanup.

8. Promises vs. Async/Await#

Promise:

JAVASCRIPT
fetchUser()
  .then((user) => {
    return fetchPosts(user.id);
  })
  .then(console.log);

Async/Await#

JAVASCRIPT
const user = await fetchUser();

const posts = await fetchPosts(user.id);

console.log(posts);

Cleaner syntax.

Important Interview Point#

Async/Await:

Does Not Replace Promises

Instead:

Built On Top Of Promises

9. Common Interview Q&A#

Can a Promise Change State Twice?#

No.

Example:

JAVASCRIPT
resolve("A");
resolve("B");
reject("Error");

Only the first state change matters.

Does Creating a Promise Make It Asynchronous?#

No.

The Promise constructor executes immediately.

What Queue Do Promise Callbacks Use?#

Microtask Queue

What Happens If a Then Callback Returns a Promise?#

The next .then() waits for that Promise to settle.

What Happens If a Then Callback Throws?#

The Promise becomes rejected.

The error propagates to the nearest .catch().

Senior-Level Interview Answer#

A Promise is an object that represents the eventual completion or failure of an asynchronous operation. It can be in one of three states: pending, fulfilled, or rejected. Promises solve callback hell by providing a structured way to compose asynchronous operations through chaining. Methods such as then, catch, and finally allow success handling, error handling, and cleanup. Promise callbacks are scheduled in the Microtask Queue, which gives them higher priority than timer callbacks in the Event Loop. Modern async/await syntax is built on top of Promises and provides a cleaner way to write asynchronous code.

Common Interview Mistakes#

❌ "Promises are asynchronous."

Correct:

Promise callbacks are asynchronous.
The Promise constructor executes immediately.

❌ "Async/Await replaces Promises."

Correct:

Async/Await is built on top of Promises.

❌ "Promise.all waits even if one Promise fails."

Correct:

Promise.all rejects immediately
when any Promise rejects.

❌ "Promises use the Callback Queue."

Correct:

Promises use the Microtask Queue.

Promise API Summary#

APIBehavior
Promise.resolve()Creates fulfilled Promise
Promise.reject()Creates rejected Promise
Promise.all()Waits for all Promises
Promise.allSettled()Waits for all results
Promise.race()First settled Promise wins
Promise.any()First fulfilled Promise wins
finally()Runs regardless of outcome

Key Takeaways#

  • Promise States: A Promise represents an async operation that exists in one of three states: pending, fulfilled, or rejected.
  • Chaining: Methods like then and catch return new Promises, preventing nesting and resolving callback hell.
  • Event Loop Priority: Promise callbacks are processed inside the Microtask Queue, prioritizing them over standard timers.
  • Parallel APIs: Use Promise.all for parallel operations, and Promise.allSettled to fetch resources without cascading failures.