JavaScript Async

Promises

Understand asynchronous programming, promise states, chaining, and error handling.

Learn how JavaScript handles asynchronous operations before async/await.

Pending

pending

The async operation has started, but it has not completed yet.

Fulfilled

fulfilled

The operation completed successfully and resolved with a value.

Rejected

rejected

The operation failed and rejected with an error reason.

Beginner Mental Model

A Promise Means: I Will Give You a Result Later

Promises help JavaScript represent work that has started but has not finished yet, like API calls, file uploads, timers, or payments.

Food Delivery Analogy

When you order food, you do not get the result immediately. The order is pending. Later it either succeeds, or it fails. A Promise works the same way for asynchronous JavaScript work.

1

Order placed

Promise is pending while the kitchen prepares the food.

2

Food delivered

Promise is fulfilled with the meal.

3

Order cancelled

Promise is rejected with a reason.

Promise state flow

Promise Created
      |
      v
Pending
      |
      +-------------------+
      |                   |
      v                   v
Fulfilled             Rejected
      |                   |
      v                   v
.then(value)         .catch(error)
      |
      v
.finally(cleanup)
const request = fetch("/api/products");

request
  .then((response) => response.json())
  .then((products) => renderProducts(products))
  .catch((error) => showError(error))
  .finally(() => hideLoader());
Creating Promises

resolve() Means Success, reject() Means Failure

A Promise constructor receives resolve and reject functions. Call resolve for success and reject for failure.

Successful Promise
const promise = new Promise((resolve, reject) => {
  resolve("Payment Successful");
});

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

Output

Payment Successful

Failed Promise
const promise = new Promise((resolve, reject) => {
  reject("Payment Failed");
});

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

Output

Payment Failed

Real-World Flows

Bank Transfers and API Calls Work Like Promises

You start the operation now, then handle success or failure later without freezing the UI.

Bank Transfer

Bank transfer flow

Transfer Started
      |
      v
Processing...
      |
      +---------------------+
      |                     |
      v                     v
Money Sent           Insufficient Balance
API Call

API call flow

Request Sent
      |
      v
Waiting...
      |
      v
Server Responds
      |
      v
Products Received
      |
      v
Display Products
fetch("/api/products")
  .then((response) => response.json())
  .then((products) => {
    console.log(products);
  })
  .catch((error) => {
    console.log(error);
  });
Promise Methods

.then(), .catch(), and .finally()

These methods are the main way to consume a Promise before using async/await.

Promise lifecycle

Create Promise
      |
      v
Pending
      |
      v
Success?
  |       |
 Yes      No
  |       |
  v       v
resolve() reject()
  |       |
  v       v
.then()  .catch()

.then()

Runs when the Promise is fulfilled.

Promise.resolve("Hello").then((result) => console.log(result));

.catch()

Runs when the Promise is rejected.

Promise.reject("Error").catch((error) => console.log(error));

.finally()

Runs whether the Promise succeeds or fails, usually for cleanup.

fetch("/api/products").finally(() => console.log("Hide Loader"));
Chain Mental Model

Promise Chains Pass Results Forward

Each then() can return a value or another Promise. The next step waits for that result before running.

Promise chain

fetchUser()
    |
    v
.then(fetchOrders)
    |
    v
.then(fetchRecommendations)
    |
    v
.then(renderPage)
    |
    v
.catch(handleError)

What to Remember

then() handles a fulfilled Promise.

catch() handles a rejected Promise.

finally() runs after success or failure.

Returning a Promise makes the next then() wait.

A settled Promise cannot change state again.

Promises vs Callbacks

Promises Help Avoid Callback Hell

Nested callbacks become difficult to read and maintain. Promise chains make the async flow flatter and easier to handle.

Callback Hell
getUser(function(user) {
  getOrders(user, function(order) {
    getPayment(order, function(payment) {
      console.log(payment);
    });
  });
});
Cleaner Promise Chain
getUser()
  .then(getOrders)
  .then(getPayment)
  .then(console.log)
  .catch(console.error);
Event Loop

Promise Callbacks Run in the Microtask Queue

Synchronous code runs first. Promise callbacks run after the call stack is empty and before macrotasks like setTimeout.

console.log("1");

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

console.log("3");

Output

1

3

2

Why?

1

console.log("1") runs immediately.

2

Promise .then() is placed in the Microtask Queue.

3

console.log("3") runs immediately.

4

The Event Loop runs microtasks, so console.log("2") runs last.

Checkout Flow

Real-World Example: Payment Promise

A payment Promise keeps checkout responsive while the authorization request is processed.

console.log("Validate Card");

createPayment()
  .then(() => {
    console.log("Payment Approved");
  })
  .catch(() => {
    console.log("Payment Failed");
  })
  .finally(() => {
    console.log("Hide Spinner");
  });

console.log("Show Spinner");

Possible Outputs

Success

Validate Card

Show Spinner

Payment Approved

Hide Spinner

Failure

Validate Card

Show Spinner

Payment Failed

Hide Spinner

Promise Lifecycle

Promise Lifecycle Visualizer

A Promise begins pending, then settles into either fulfilled or rejected.

Applications

Real World Promise Patterns

Promises power everyday async workflows across frontend, backend, and commerce systems.

API Calls

Fetch product, account, and content data without blocking the UI.

File Upload

Track upload progress, completion, and upload failures with promise states.

Payment Processing

Chain validation, authorization, capture, and error recovery safely.

Order Placement

Coordinate checkout calls and render confirmation only after success.

Analytics Requests

Send non-blocking events while keeping the shopping journey responsive.

Enterprise eCommerce

Commerce Promise Examples

Checkout and product journeys are full of async operations that need clean state, success, and failure paths.

Product Search

Debounced search returns a Promise that resolves with matching products.

Inventory Lookup

Parallel inventory calls resolve before rendering availability badges.

Checkout Validation

Shipping, payment, and address promises determine whether checkout can continue.

Payment Authorization

Rejected promises surface recoverable payment errors.

Order Confirmation

Fulfilled order placement promises render receipt and confirmation details.

Promise vs async/await

Same Behavior, Different Syntax

async/await is built on top of Promises and often makes asynchronous code easier to read.

// Using Promises
fetch("/api/products")
  .then((response) => response.json())
  .then((data) => console.log(data))
  .catch((error) => console.log(error));

// Using async/await
async function getProducts() {
  try {
    const response = await fetch("/api/products");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.log(error);
  }
}
Why Promises?

Why Do We Need Promises?

Promises make asynchronous JavaScript easier to read, chain, and recover from.

Without Promises

Deeply nested callbacks

Harder error handling

Less readable code

With Promises

Cleaner asynchronous code

Better error handling with .catch()

Easy chaining with .then()

Works seamlessly with async/await

Interview Ready

Promises in 30 Seconds

Use this answer when someone asks what a Promise is.

A Promise is a JavaScript object that represents the eventual result of an asynchronous operation. It starts in the Pending state and eventually becomes either Fulfilled by calling resolve() or Rejected by calling reject(). We consume Promises using .then(), .catch(), and .finally(), or with async/await. Promise callbacks are executed through the Microtask Queue, which the Event Loop processes before Macrotasks like setTimeout().

Common Interview Questions

Promises Interview Prep

Detailed answers for Promise fundamentals, state transitions, chaining, and concurrency.

Q1

What is a Promise?

A Promise is a JavaScript object representing the eventual result of an asynchronous operation. It starts pending, then becomes fulfilled with a value or rejected with an error.

Q2

What are Promise States?

A Promise has three states: pending, fulfilled, and rejected. Once it settles as fulfilled or rejected, that state is final and cannot change.

Q3

Difference between Promise.all and Promise.race?

Promise.all waits for every promise to fulfill and rejects if any promise rejects. Promise.race settles as soon as the first promise fulfills or rejects.

Q4

How does Error Handling work?

Errors thrown inside a then callback or promises that reject move to the nearest catch handler. finally runs after settlement and is useful for cleanup like hiding loaders.

Q5

What is Promise Chaining?

Promise chaining connects async steps by returning a value or another promise from each then callback. The next then waits for that returned promise before running.