DEV Community

punavwalke
punavwalke

Posted on

Why Promises Exist in JavaScript (The Questions I Had as a Beginner)

🔥 The Question That Started It All

When I started learning async JavaScript, I didn’t just want to know how promises work.

I kept asking:

👉 Why do promises even exist?

Before learning syntax, I wanted to understand:

  • What problem are they solving?
  • Was the old approach really that bad?
  • Why was this design needed in the first place?

🤔 The Real Questions I Had

Instead of jumping into syntax, these were my actual doubts:

  • Was callback hell really such a big problem?
  • Why didn’t JavaScript just improve callbacks?
  • Who designed promises and why?
  • Why does .then() look like this?
  • How does async execution even work in a single-threaded language?

⚠️ The Problem: Async Code with Callbacks

Before promises, async code often looked like this:

let id="example"
setTimeout(() => {
  console.log("Step 1");

  setTimeout(() => {
    console.log("Step 2");

    setTimeout(() => {
      console.log("Step 3");
    }, 1000);

  }, 1000);

}, 1000);
Enter fullscreen mode Exit fullscreen mode

At First Glance

This works fine.

So my initial thought was:

“Why is this considered a problem?”


💥 The Real Issue (At Scale)

Now imagine real-world applications:

  • Multiple API calls
  • Conditional flows
  • Error handling at each step
  • Retry mechanisms

This quickly turns into:

  • Deep nesting
  • Hard-to-read logic
  • Difficult debugging
  • Hard to modify safely

👉 The issue is not indentation—it’s loss of flow clarity


💡 Key Realization

Callback hell is not a problem in small code.

👉 It becomes a problem when the system grows.


🎯 The Deeper Problem: Inversion of Control

This is the real hidden issue behind callbacks.


🧠 What It Means

With callbacks, you pass your logic to another function:

let id="example"
doSomething(() => {
  console.log("I run later");
});
Enter fullscreen mode Exit fullscreen mode

Now you are trusting that function to:

  • Call your code correctly
  • Call it only once
  • Call it at the right time
  • Not break execution flow

👉 You are no longer in control of execution.

This is called Inversion of Control.


⚠️ Why This Becomes a Problem

In small apps, it’s manageable.

But in large systems:

  • Many callbacks depend on each other
  • Execution flow becomes distributed
  • Debugging becomes unpredictable

👉 You lose confidence in when and how code runs


💡 The Idea Behind Promises

Promises were introduced to solve two core problems:

1. Callback Hell (Structure Problem)

2. Inversion of Control (Trust Problem)


⚙️ 1. Fixing Callback Hell: Making Code Readable

❌ Callback Style

let id="callbackstyle"
api1((res1) => {
  api2(res1, (res2) => {
    api3(res2, (res3) => {
      console.log(res3);
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

✅ Promise Style

let id="promisestyle"
api1()
  .then(res1 => api2(res1))
  .then(res2 => api3(res2))
  .then(res3 => {
    console.log(res3);
  });
Enter fullscreen mode Exit fullscreen mode

💡 What Changed?

Instead of nested logic, we now have:

👉 A flat, readable flow

It becomes:

step 1 → step 2 → step 3


🔒 2. Fixing Inversion of Control: Restoring Predictability

⚠️ With Callbacks

let id="problem"
doSomething(() => {
  console.log("Executed later");
});
Enter fullscreen mode Exit fullscreen mode

You don’t control:

  • When it runs
  • How many times it runs
  • Whether it runs correctly

💡 With Promises

Promises give a structured contract:

  • Either resolved or rejected
  • Executes exactly once
  • Chaining is predictable
let id="solution"
api1()
  .then(res1 => api2(res1))
  .then(res2 => api3(res2))
  .catch(err => console.error(err));
Enter fullscreen mode Exit fullscreen mode

🧠 Key Shift

With promises:

👉 You don’t hand over control of execution
👉 You define a structured sequence of steps


🔗 Why .then()?

This confused me initially.

Why:

let id="thenexample"
doSomething().then(nextStep);
Enter fullscreen mode Exit fullscreen mode

Instead of:

let id="directcall"
doSomething(nextStep);
Enter fullscreen mode Exit fullscreen mode

💡 Insight

.then() creates a pipeline of execution steps, not just a function call.


🧩 Compare the Two Approaches

❌ Nested Callbacks

let id="nestedfinal"
step1(step2(step3()));
Enter fullscreen mode Exit fullscreen mode

✅ Promise Chain

let id="chainfinal"
step1()
  .then(step2)
  .then(step3);
Enter fullscreen mode Exit fullscreen mode

🎯 What This Solves

  • Flat structure
  • Better readability
  • Easier debugging
  • Predictable execution flow

🤯 What Finally Clicked for Me

At this point, everything started connecting:

  • Callbacks break at scale
  • Promises bring structure
  • .then() creates a pipeline
  • Execution flow becomes predictable

👉 It’s all part of one system design


👀 What About async/await?

Just when promises started making sense, I discovered async/await.

It made things even cleaner—but then I realized:

👉 It is built on top of promises.

That’s a topic for the next part.


🧠 Final Thought

If there’s one lesson from all of this:

Don’t just ask:

“How do I use this?”

Ask:

“Why does this exist?”

👉 That’s where real understanding begins.


🔚 Quick Summary

  • Callbacks become hard to manage at scale
  • Promises solve structure and control issues
  • .then() creates a clean execution pipeline
  • Promises restore predictability in async code

Top comments (0)