DEV Community

Cover image for JavaScript Promises Explained — With a Real Online Shopping Flow
DHANRAJ S
DHANRAJ S

Posted on

JavaScript Promises Explained — With a Real Online Shopping Flow

Hey!

You've probably seen the word Promise in JavaScript and thought —

"What is this? Why does it exist? Why can't JavaScript just... do things normally?"

Fair question. Let's fix that today.

We're going to use a real online shopping flow to understand Promises — step by step, together.

And by the end, this code will make complete sense to you.


1. What Is a Promise? (Before We Touch the Code)

Imagine you order food online.

The app doesn't freeze while your food is being made. You go about your day. When it's ready — you get a notification.

That notification? That's a Promise resolving.

In JavaScript, a Promise is an object that says:

"I'll give you a result — but not right now. Give me some time. I'll get back to you."

A Promise has 3 possible states:

  • Pending — still working, no result yet
  • Resolved — finished successfully, here's your result
  • Rejected — something went wrong, here's the error

Now let's see this in real code.


2. The Full Code — Look at It Once

function checkInternet() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const online = true;
      if (online) {
        resolve("Internet connected ✅");
      } else {
        reject("No internet connection ❌");
      }
    }, 1000);
  });
}

function loginUser() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("User logged in ✅");
    }, 1000);
  });
}

function checkStock() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const stockAvailable = true;
      if (stockAvailable) {
        resolve("Product in stock ✅");
      } else {
        reject("Product out of stock ❌");
      }
    }, 1500);
  });
}

function processPayment() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const paymentSuccess = true;
      if (paymentSuccess) {
        resolve("Payment successful ✅");
      } else {
        reject("Payment failed ❌");
      }
    }, 2000);
  });
}

function placeOrder() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("Order placed successfully ✅");
    }, 1000);
  });
}

function sendConfirmation() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("Confirmation message sent ✅");
    }, 1000);
  });
}

checkInternet()
  .then(result => {
   console.log(result);
   return loginUser();
   })
  .then(result => {
   console.log(result); 
   return checkStock();
   })
  .then(result => {
   console.log(result);
   return processPayment();
   })
  .then(result => {
   console.log(result);
   return placeOrder(); })
  .then(result => {
   console.log(result);
   return sendConfirmation();
   })
  .then(result => {
   console.log(result);
   console.log("Order completed successfully!");
   })
  .catch(error => {
   console.log("❌ Error:", error);
   });
Enter fullscreen mode Exit fullscreen mode

Looks long, right?

Don't worry. We'll break it into small pieces. Each piece is actually very simple.


3. What's Actually Happening Here? — The Big Picture

This code simulates a complete online order flow.

Think of it like this — when you buy something online, the app runs these steps one after another:

Check Internet
     ↓
  Login User
     ↓
 Check Stock
     ↓
Process Payment
     ↓
  Place Order
     ↓
Send Confirmation
     ↓
   Done!
Enter fullscreen mode Exit fullscreen mode

Each step waits for the previous one to finish before starting.

That's exactly what Promises and .then() do.


4. Let's Understand One Promise First

Let's take the simplest one — checkInternet.

function checkInternet() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const online = true;
      if (online) {
        resolve("Internet connected ✅");
      } else {
        reject("No internet connection ❌");
      }
    }, 1000);
  });
}
Enter fullscreen mode Exit fullscreen mode

Let's read this line by line 👇

return new Promise((resolve, reject) => {
→ We're creating a new Promise. It takes two things — resolve and reject. These are functions JavaScript gives us.

setTimeout(() => { ... }, 1000)
→ Wait 1 second before doing anything. This simulates real network delay.

const online = true
→ This is our fake condition. In a real app, this would check your actual internet.

if (online) { resolve("Internet connected ✅") }
→ If internet is available — call resolve. Promise is successful. ✅

else { reject("No internet connection ❌") }
→ If no internet — call reject. Promise failed. ❌

That's it. That's one complete Promise.


*5. Quick Question for You *

Look at loginUser:

function loginUser() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("User logged in ✅");
    }, 1000);
  });
}
Enter fullscreen mode Exit fullscreen mode

Did you notice something different from checkInternet?

Take a second. Look carefully.

...

It only has resolve — no reject.

Why? Because in this code, login always succeeds. There's no failure case written for it. So we only need resolve.

Simple — but important to notice. 👀


6. Now Let's Talk About resolve and reject

These two are the heart of every Promise.

resolve → "Everything went well. Here's the result. Move to the next step."

reject → "Something went wrong. Stop here. Jump to .catch()."

Here's a clear picture of which functions can fail and which always succeed:

Function Can Fail? Why
checkInternet() ✅ Yes Internet might be off
loginUser() ❌ No Always resolves
checkStock() ✅ Yes Item might be out of stock
processPayment() ✅ Yes Payment might fail
placeOrder() ❌ No Always resolves
sendConfirmation() ❌ No Always resolves

Functions that can fail have both resolve and reject.
Functions that always succeed only have resolve.


7. What Does .then() Actually Do?

Now the fun part. Look at how we run all 6 steps:

checkInternet()
  .then(result => {
    console.log(result);
    return loginUser();
  })
  .then(result => {
    console.log(result);
    return checkStock();
  })
  .then(result => {
    console.log(result);
    return processPayment();
  })
  ...
Enter fullscreen mode Exit fullscreen mode

Let's understand .then() clearly.

.then() runs only when the previous Promise resolves successfully.

It receives the resolved value as result — and you can use it however you want.

Then — you return the next Promise inside it. That's what creates the chain.

Think of it like a relay race :

Runner 1 finishes → passes the baton → Runner 2 starts → passes the baton → Runner 3 starts...

Each .then() is one runner. Each return is passing the baton.


8. Step-by-Step Output — Let's Walk Through It Together

Here's what happens when you run this code:

Waits 1 second...

Internet connected ✅
Enter fullscreen mode Exit fullscreen mode

Waits 1 more second...

User logged in ✅
Enter fullscreen mode Exit fullscreen mode

Waits 1.5 seconds...

Product in stock ✅
Enter fullscreen mode Exit fullscreen mode

Waits 2 seconds...

Payment successful ✅
Enter fullscreen mode Exit fullscreen mode

Waits 1 second...

Order placed successfully ✅
Enter fullscreen mode Exit fullscreen mode

Waits 1 second...

Confirmation message sent ✅
 Order completed successfully!
Enter fullscreen mode Exit fullscreen mode

Each step runs one at a time, in order. The next step never starts before the previous one finishes.

That's the power of Promise chaining.


9. What About .catch()? — The Safety Net

.catch(error => {
  console.log("❌ Error:", error);
});
Enter fullscreen mode Exit fullscreen mode

This is your safety net.

If any Promise in the entire chain calls reject — the whole chain stops and jumps straight to .catch().

Let's say you change this in checkStock:

const stockAvailable = false; //  changed to false
Enter fullscreen mode Exit fullscreen mode

The output would be:

Internet connected ✅
User logged in ✅
❌ Error: Product out of stock ❌
Enter fullscreen mode Exit fullscreen mode

Everything after checkStock — payment, order, confirmation — none of it runs.

One failure. Whole chain stops. .catch() handles it.

Clean. Safe. Controlled.


*10. Small Challenge for You *

Before you move on — try this mentally.

What happens if you change this line in processPayment:

const paymentSuccess = false; // 
Enter fullscreen mode Exit fullscreen mode

What will the output be?

Think about it.

...

Answer:

Internet connected ✅
User logged in ✅
Product in stock ✅
❌ Error: Payment failed ❌
Enter fullscreen mode Exit fullscreen mode

The chain ran fine until payment failed. Then .catch() caught it. Order never placed. Confirmation never sent.

Makes sense, right? That's exactly how real apps work.


Quick Summary — 5 Things to Remember

  1. A Promise = a future result — it's either going to succeed (resolve) or fail (reject). Not both.

  2. resolve = success — moves to the next .then() in the chain.

  3. reject = failure — skips everything and jumps straight to .catch().

  4. .then() chains steps — each step waits for the previous one to finish before starting.

  5. .catch() is your safety net — one place to handle any error from anywhere in the chain.


Promises feel confusing at first because they're about time — things happening later, not immediately.

But once you see it as a relay race — one step finishing and passing to the next — it starts to click.

Try running this code yourself. Change true to false in different places and watch what happens. That hands-on experiment will teach you more than any explanation.

Got a question? Drop it in the comments. No question is too basic — ask away.


Thanks for reading! Keep building, one Promise at a time.

Top comments (0)