Skip to main content

Command Palette

Search for a command to run...

JavaScript Promises Explained for Beginners

Updated
•5 min read•View as Markdown
K
Hi, I'm Kanishka Shashi. I'm a Computer Science (AI & ML) student passionate about building, analyzing, and explaining technology. This blog is where I simplify complex concepts into practical, beginner-friendly guides. You'll find articles on Web Development, System Design, AI & LLMs, Data Analytics, SQL, Git/GitHub, and software engineering best practices. I enjoy exploring how modern technologies work under the hood and sharing insights through real-world examples and hands-on projects. Whether you're a student, developer, or tech enthusiast, I hope these articles help you learn something new and build better software. Learning in public. Building consistently. Sharing everything I discover.

If you’ve started learning asynchronous JavaScript, you’ve probably heard about Promises. At first, they may seem confusing—but once you understand them, they become one of the most powerful and essential tools in JavaScript.

Promises help you write cleaner, more readable, and manageable asynchronous code.

In this blog, we’ll cover:

  • What problem promises solve

  • Promise states (pending, fulfilled, rejected)

  • Basic promise lifecycle

  • Handling success and failure

  • Promise chaining

  • Comparison with callbacks

  • Real-world examples


🔹 What Problem Do Promises Solve?

Before promises, JavaScript developers relied heavily on callbacks for handling asynchronous operations.


❌ Callback Example (Callback Hell)

getData(function(a) {
  processData(a, function(b) {
    saveData(b, function(c) {
      console.log(c);
    });
  });
});

👉 This is called callback hell:

  • Hard to read 😵

  • Difficult to debug 🔍

  • Nested and messy


💡 Problem Summary

  • Deep nesting

  • Poor readability

  • Hard error handling


🚀 Solution: Promises

A Promise represents a value that may be available now, later, or never.

👉 Think of it as a “future value”.


🔹 What is a Promise?

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


✅ Basic Syntax

let promise = new Promise((resolve, reject) => {
  // async operation
});
  • resolve() → success

  • reject() → failure


🔹 Promise States

Every promise goes through three states:


1. ⏳ Pending

  • Initial state

  • Operation is still running


2. ✅ Fulfilled

  • Operation completed successfully

  • resolve() is called


3. ❌ Rejected

  • Operation failed

  • reject() is called


🔍 Diagram Idea: Promise Lifecycle

         Pending
        /       \
   Fulfilled   Rejected
   (resolve)   (reject)

🔹 Basic Promise Example

let myPromise = new Promise((resolve, reject) => {
  let success = true;

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

🔹 Handling Success and Failure

We use .then() and .catch() to handle promise results.


✅ Example

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

🔍 Output

Task completed

💡 Key Idea

  • .then() → handles success

  • .catch() → handles errors


🔹 Promise Lifecycle (Step-by-Step)

Let’s understand what happens behind the scenes:


console.log("Start");

let promise = new Promise((resolve) => {
  setTimeout(() => resolve("Done!"), 2000);
});

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

console.log("End");

🔍 Output

Start
End
(After 2 seconds)
Done!

🔍 Explanation

  1. Promise starts → Pending

  2. setTimeout runs asynchronously

  3. Code continues → "End" prints

  4. After 2 seconds → Promise resolves

  5. .then() executes


🔹 Promise Chaining

One of the biggest advantages of promises is chaining.


❌ Without Chaining (Messy)

getData(function(a) {
  processData(a, function(b) {
    console.log(b);
  });
});

✅ With Promise Chaining

getData()
  .then(a => processData(a))
  .then(b => console.log(b))
  .catch(err => console.log(err));

💡 Benefits

  • Cleaner structure

  • No deep nesting

  • Easy to follow


🔍 Diagram Idea: Promise Chaining Flow

Start → then → then → then → catch

🔹 Real-World Example (API Call)

fetch("https://jsonplaceholder.typicode.com/posts")
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.log("Error:", error));

💡 What’s Happening?

  • fetch() returns a promise

  • First .then() handles response

  • Second .then() handles data

  • .catch() handles errors


🔹 Promises vs Callbacks


🔍 Diagram Idea: Callback vs Promise

Callbacks:
Task → callback → callback → callback

Promises:
Task → then → then → catch

📊 Comparison Table

Feature Callbacks ❌ Promises ✅
Readability Poor (nested) Clean and structured
Error Handling Difficult Easy with .catch()
Debugging Hard Easier
Flow Control Messy Predictable

🔹 Common Mistakes to Avoid


❌ Not Returning Promises

.then(data => {
  processData(data); // forgot return
})

👉 Always return if chaining.


❌ Missing Catch Block

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

👉 Always handle errors!


❌ Nested Promises (Avoid This)

getData().then(data => {
  processData(data).then(result => {
    console.log(result);
  });
});

👉 Use chaining instead.


🔹 Why Promises Improve Readability

Promises:

  • Flatten nested code

  • Make logic easier to follow

  • Separate success and error handling clearly

👉 This makes your code more professional and maintainable.