ReviseAlgo Logo

Asynchronous JavaScript

Promises

Master JavaScript Promises. Learn Promise states, how to wrap asynchronous operations, chain .then() and .catch(), and manage error flow.

Last Updated: July 15, 2026 10 min read

1. Introduction

A Promise is a proxy placeholder object representing the eventual completion (or failure) of an asynchronous operation and its resulting value. Promises solve the inversion of control issues of callbacks by providing a standardized API for handling asynchronous events.

2. Why It Matters

Promises are the foundation of modern asynchronous JavaScript APIs (like the fetch API). They allow you to chain operations, propagate errors to a single catch block, and handle asynchronous events cleanly.

3. Real-World Analogy

Think of a Pager at a Restaurant:

  • Pending State: You order food. The host hands you a plastic pager (the Promise). Your food is not ready, but you have a placeholder for it.
  • Fulfilled State: Your food is ready. The pager buzzes and flashes (resolves the promise). You swap the pager for your meal (resolved value).
  • Rejected State: The kitchen runs out of ingredients. The pager flashes red (rejects the promise), signaling that the order failed. You check in at the desk to handle the error.

4. Promise States

A Promise is always in one of three mutually exclusive states:
Pending: The initial state. The asynchronous operation is still running.
Fulfilled: The operation completed successfully. The promise has a resolved value.
Rejected: The operation failed. The promise has a rejection reason (error).
Once a Promise resolves or rejects, its state is settled and cannot change.

5. Chaining & Error Propagation

Methods like .then() and .catch() always return a new Promise, allowing you to flatten asynchronous pipelines into a single chain. Errors propagate down the chain automatically until they reach the first .catch() block.

6. Practical Example

This script shows how to wrap a classic callback-based timer inside a Promise:

7. Common Mistakes

  • Forgetting to return nested Promises: If you omit the return keyword inside a .then() callback, the next .then() block in the chain runs immediately with the value undefined instead of waiting for the nested promise to resolve.

8. Quick Quiz

Q1: Can a Promise change its state after it has transitioned from Pending to Fulfilled?

A) Yes, if we call reject() later

B) No, the state of a settled Promise is immutable

Answer: B — Once a Promise resolves or rejects, its state is settled and cannot be modified again.

9. Scenario-Based Challenge

The API Retry Policy:

You write a network fetcher. If the network request fails, you want to retry the request up to 3 times before finally rejecting the Promise. Design a recursive Promise wrapper to implement this retry logic.

10. Debugging Exercise

Explain why this error handler is never triggered, and how to fix it:

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    throw new Error('Async crash'); // throws inside a background timer!
  }, 100);
});

p.catch(err => console.log('Caught: ' + err.message)); // never runs!

View Solution

Diagnosis: The exception is thrown inside a setTimeout callback, which runs in a different execution context than the Promise constructor. As a result, the Promise cannot catch the error. The error is thrown globally as an uncaught exception, and the Promise remains in a pending state indefinitely.

Fix: Catch the error inside the timeout callback, and reject the Promise explicitly:

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    try {
      throw new Error('Async crash');
    } catch (e) {
      reject(e); // Reject the Promise explicitly
    }
  }, 100);
});

11. Interview Questions

🟢 Q1: Describe the three states of a Promise and the concept of settling.

Answer:
Pending: The initial state before the operation finishes.
Fulfilled: The operation completed successfully. The Promise resolves to a value.
Rejected: The operation failed. The Promise rejects with a reason (error).
Settling: A Promise is settled when it transitions to either the fulfilled or rejected state. Once settled, its state and value/error are locked and cannot change.

12. Production Considerations

  • Handle Unhandled Rejections: Always add a .catch() block to your Promise chains. In Node.js or browser production environments, monitor global unhandled rejection events (like the unhandledrejection window listener) to prevent silent application crashes.