Skip to main content

Command Palette

Search for a command to run...

Understanding Promises in JavaScript

Updated
6 min readView as Markdown
Understanding Promises in JavaScript

Asynchronous programming is one of the most important concepts in JavaScript.

Operations like:

  • API requests

  • Database queries

  • File reading

  • Timers

  • Authentication

  • Network communication

do not complete immediately.

Originally, JavaScript handled asynchronous operations using callbacks.

But as applications became larger, callback-based code became difficult to manage.

To solve this problem, JavaScript introduced:

Promises

Promises made asynchronous code cleaner, more readable, and easier to maintain.

In this article, we will understand:

  • What problem promises solve

  • Promise states

  • Promise lifecycle

  • Handling success and failure

  • Promise chaining

  • Why promises improved async programming


The Problem Before Promises

Before promises, asynchronous operations mostly used callbacks.

Example:

readFile("data.txt", (err, data) => {
  if (err) {
    console.log(err);
  } else {
    fetchUser(data, (err, user) => {
      if (err) {
        console.log(err);
      } else {
        saveUser(user, (err) => {
          if (err) {
            console.log(err);
          }
        });
      }
    });
  }
});

As more async operations were added, the code became deeply nested.

This created:

  • Poor readability

  • Difficult debugging

  • Complex error handling

This problem became known as:

Callback Hell

Why Promises Were Introduced

Promises were introduced to improve:

  • Readability

  • Async flow control

  • Error handling

  • Maintainability

Promises allow asynchronous operations to be handled in a cleaner and more structured way.


What Is a Promise?

A promise represents:

A future value

It is an object that eventually:

  • Resolves successfully

  • Fails with an error

Think of a promise as:

"A placeholder for a result that will arrive later."

Real-World Analogy

Imagine ordering food online.

After placing the order:

  • Food is not available immediately

  • Restaurant promises delivery later

Possible outcomes:

  • Food delivered successfully

  • Delivery failed

This is similar to JavaScript promises.


Creating a Basic Promise

Example:

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

  if (success) {
    resolve("Task completed");
  } else {
    reject("Task failed");
  }
});

Understanding resolve and reject

Inside promises:


resolve()

Used when operation succeeds.

Example:

resolve("Success");

reject()

Used when operation fails.

Example:

reject("Error occurred");

Promise States

A promise can exist in three states.


Pending

Initial state.

Operation is still running.


Fulfilled

Operation completed successfully.


Rejected

Operation failed.


Promise Lifecycle Diagram

           PENDING
          /       \
         /         \
        V           V
FULFILLED       REJECTED

A promise starts pending and later settles into one final state.


Promise Lifecycle Example

Example:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Completed");
  }, 2000);
});

Flow:

  1. Promise created

  2. State = pending

  3. Wait 2 seconds

  4. resolve() executes

  5. State = fulfilled


Handling Promise Success

Success is handled using:

.then()

Example:

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

Output:

Completed

Handling Promise Failure

Errors are handled using:

.catch()

Example:

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

This handles rejected promises.


Handling Success and Failure Together

Example:

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

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

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

Output:

Something went wrong

Promise Execution Flow

Promise Created
       |
       V
Pending State
       |
       V
Success? -----> Yes -----> .then()
       |
       No
       |
       V
    .catch()

Why Promises Improved Readability

Promises replaced deeply nested callbacks with cleaner chaining.


Callback Version

getUser((user) => {
  getPosts(user, (posts) => {
    getComments(posts, (comments) => {
      console.log(comments);
    });
  });
});

Promise Version

getUser()
  .then(getPosts)
  .then(getComments)
  .then(console.log)
  .catch(console.error);

This structure is much cleaner.


Callback vs Promise Comparison

CALLBACKS

Task
  |
  V
Nested Task
    |
    V
More Nesting


PROMISES

Task
  |
  V
.then()
  |
  V
.then()
  |
  V
.catch()

Promises flatten async workflows.


Understanding Promise Chaining

One major advantage of promises is chaining.

Each .then() can return another promise.

Example:

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

This creates sequential async execution without deep nesting.


Why Promise Chaining Matters

Chaining improves:

  • Readability

  • Flow control

  • Sequential execution

  • Error handling

Large async workflows become easier to manage.


Automatic Error Propagation

One powerful promise feature:

Errors automatically travel down the chain.

Example:

fetchUser()
  .then(fetchPosts)
  .then(savePosts)
  .catch(console.error);

Any failure triggers .catch().

This simplified async error handling significantly.


Common Promise Methods

Method Purpose
.then() Handle success
.catch() Handle errors
.finally() Execute always
Promise.all() Run multiple promises
Promise.race() First completed promise

Understanding finally()

Example:

promise
  .then(() => {
    console.log("Success");
  })
  .catch(() => {
    console.log("Error");
  })
  .finally(() => {
    console.log("Completed");
  });

finally() executes regardless of success or failure.


Why Promises Became Important

Promises solved major async programming problems:

  • Callback hell

  • Nested logic

  • Scattered error handling

  • Poor readability

They became the foundation for modern async JavaScript.


Promises and Async/Await

Modern async/await syntax is actually built on top of promises.

Example:

async function getData() {
  const data = await fetchUser();

  console.log(data);
}

Internally, async/await still uses promises.


Common Beginner Mistakes


Forgetting return in Chains

Bad example:

.then(() => {
  fetchData();
})

Better:

.then(() => {
  return fetchData();
})

Missing catch()

Unhandled promise errors can crash applications.

Always handle rejections properly.


Mixing Callbacks and Promises Unnecessarily

Avoid mixing patterns unless required.


Common Interview Questions

What Is a Promise?

An object representing a future async result.


What Are Promise States?

  • Pending

  • Fulfilled

  • Rejected


Difference Between Callback and Promise

Promises improve readability and error handling.


What Does .then() Do?

Handles successful promise resolution.


Real-World Importance

Promises are used everywhere in modern JavaScript:

  • API requests

  • Database operations

  • Authentication systems

  • File uploads

  • Node.js backends

  • React applications

Understanding promises is essential before learning async/await.