Skip to main content

Command Palette

Search for a command to run...

JS Promises Explained for Beginners

Updated
•5 min read•View as Markdown

What Problem Promises Solve

JavaScript is asynchronous. This means some operations take time. Examples:

  • API calls

  • database queries

  • file handling

  • authentication

  • timers

Initially, JavaScript handled asynchronous operations using callbacks. Example:

fetchData(() => {
  getUser(() => {
    getPosts(() => {
      console.log("Done");
    });
  });
});

As applications became larger, this created a huge problem. Code became:

  • deeply nested

  • difficult to read

  • difficult to debug

  • hard to maintain

This problem became known as:

Callback Hell

Promises were introduced to solve this problem.

Real Purpose of Promises

Promises were created to:

  • improve readability

  • simplify async flow

  • handle success and failure properly

  • reduce nested callbacks

Instead of passing callbacks everywhere, promises provide a cleaner structure for asynchronous operations.

What a Promise Actually Is

A promise is simply:

An object representing a future value.

Think of it like:

  • work has started

  • result is not available yet

  • eventually it will either succeed or fail

That future result is managed by promises.

Promise States

Every promise has a state. There are mainly three states.

1. Pending

Initially, every promise starts in:

pending

This means:

operation is still running.

Example:

  • API request still loading

  • file still reading

  • timer still waiting

2. Fulfilled

If operation succeeds, promise becomes:

fulfilled

This means:

task completed successfully.

And promise returns a value.

3. Rejected

If something fails, promise becomes:

rejected

This means:

operation failed.

And promise returns an error.

Visual Understanding of Promise States

Think of ordering food online.

Pending

Food is being prepared.

Fulfilled

Food delivered successfully.

Rejected

Order failed or canceled.

That is basically how promises behave internally.

Basic Promise Lifecycle

A promise moves through states during execution.

Flow:

pending → fulfilled

or

pending → rejected

Once a promise changes state:

  • it becomes final

  • it cannot change again

Creating a Basic Promise

Example:

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

  if (success) {
    resolve("Data fetched");
  } else {
    reject("Something went wrong");
  }
});

Understanding resolve and reject

Inside promises:

resolve()

Means:

operation succeeded.

reject()

Means:

operation failed.

Handling Success and Failure

Promises provide methods for handling outcomes.

Using .then()

.then() handles successful results.

Example:

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

If promise resolves:

  • .then() executes

Using .catch()

.catch() handles errors.

Example:

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

If promise rejects:

  • .catch() executes

Complete Example

Example:

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

  if (success) {
    resolve("Data received");
  } else {
    reject("Error occurred");
  }
});

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

Promise Lifecycle in This Example

  1. Promise starts as pending

  2. Condition checks

  3. If successful → resolve()

  4. Promise becomes fulfilled

  5. .then() runs

Or:

  1. reject() runs

  2. Promise becomes rejected

  3. .catch() runs

Promise Chaining Concept

One major advantage of promises is chaining. Instead of nested callbacks:

login(() => {
  getProfile(() => {
    getPosts(() => {
      console.log("Done");
    });
  });
});

Promises allow sequential flow.

Example:

login()
  .then((user) => {
    return getProfile(user);
  })
  .then((profile) => {
    return getPosts(profile);
  })
  .then((posts) => {
    console.log(posts);
  })
  .catch((error) => {
    console.log(error);
  });

Much cleaner.

Understanding Promise Chaining

Each .then() returns another promise. This allows:

  • sequential execution

  • passing results forward

  • avoiding deep nesting

Why Chaining Matters

Without chaining:

  • async code becomes messy

  • nested structures increase

Chaining creates:

  • cleaner flow

  • better readability

  • easier debugging

Error Propagation in Chaining

One powerful thing about promises: Errors automatically travel down the chain. Example:

login()
  .then(getProfile)
  .then(getPosts)
  .catch((error) => {
    console.log(error);
  });

If any step fails:

  • .catch() handles everything

This is much cleaner than callback-based error handling.

Common Beginner Mistake

Many beginners forget:

return

inside .then().

Example:

.then((data) => {
  getUser(data);
})

Without return, chaining breaks.

Correct:

.then((data) => {
  return getUser(data);
})

Another Common Confusion

People think promises make code synchronous. They do not. Promises are still asynchronous. They simply provide:

  • better structure

  • cleaner syntax

  • easier management

Promise vs Callback Thinking

Callback Style

“Run this function later.”

Promise Style

“This operation will eventually produce a result.”

This mindset shift is important.

Why Promises Became So Important

Modern JavaScript depends heavily on promises. Especially:

  • APIs

  • databases

  • fetch requests

  • authentication

  • async operations

Even async/await internally works on top of promises.

Final Understanding

Promises were introduced to solve the readability and maintenance problems caused by nested callbacks. A promise represents a future result and can exist in three states:

  • pending

  • fulfilled

  • rejected

Using:

  • .then()

  • .catch()

developers can handle success and failure more cleanly. Promise chaining allows sequential asynchronous operations without deep nesting, making JavaScript code easier to read, debug, and maintain.

1 views