Skip to main content

Command Palette

Search for a command to run...

The Chai Stall Promise System: How Hitesh Bhaiya Manages Orders โ˜•๐Ÿ‘จโ€๐Ÿ’ป

Published
โ€ข9 min read
The Chai Stall Promise System: How Hitesh Bhaiya Manages Orders โ˜•๐Ÿ‘จโ€๐Ÿ’ป

By Saurabh Prajapati | Full-Stack Engineer @ IBM India Software Lab


Before We Start โ€” Picture This...

It's 8:15 AM. Hitesh Bhaiya's chai stall is absolutely packed.

Ten students from the cohort are standing around, all shouting their orders at once. Someone wants regular chai, someone wants masala chai, someone wants ginger chai with extra adrak, and Piyush bhai just walked in asking for samosa too.

Hitesh Bhaiya is one person. He can only do so much.

So what does he do? He gives everyone a token. A little receipt that says: "Your order is being made. I promise."

That token? That's a JavaScript Promise.

And today, we're going to understand exactly how JavaScript handles multiple async operations โ€” using the most relatable metaphor possible. Chai. Orders. Hitesh bhaiya. Let's go. โ˜•


What Even Is a Promise?

Okay, before we jump into the fancy methods, let's make sure the basics are solid.

A Promise in JavaScript is an object that represents the eventual result of an asynchronous operation. It's JavaScript's way of saying: "I don't have the answer right now, but I promise I'll get back to you."

Think of it like Hitesh bhaiya handing you an order token. At that moment, your chai isn't ready. But you have a promise that it will be. And that promise can have three outcomes:

  • Pending โ€” your order is still being made โณ

  • Fulfilled โ€” your chai is ready, come collect it! โœ…

  • Rejected โ€” sorry yaar, we ran out of milk ๐Ÿ˜ข

Here's what that looks like in code:

const chaiOrder = new Promise((resolve, reject) => {
  const chaiReady = true;

  if (chaiReady) {
    resolve("Here's your masala chai! โ˜•");
  } else {
    reject("Sorry, we ran out of milk ๐Ÿ˜ข");
  }
});

chaiOrder
  .then((message) => console.log(message))   
  .catch((error) => console.log(error));     

Simple enough, right? Now here's where it gets interesting โ€” what happens when you have multiple orders at once?

That's where Promise.all(), Promise.race(), Promise.any(), and Promise.allSettled() come in.


Promise.all() โ€” The Group Order ๐Ÿซ‚

Imagine a group of five students placing a group order. They all want chai together. They're going to sit together, study together, and they want their chai at the same time.

Hitesh bhaiya says: "Theek hai, wait karo. Jab sab ready ho jaayega, tab deta hoon."

That's Promise.all().

It waits for ALL promises to resolve. If even ONE fails, the whole thing fails.

function makeChai() {
  return new Promise((resolve) => {
    setTimeout(() => resolve("Regular Chai โ˜•"), 2000);
  });
}

function makeSamosa() {
  return new Promise((resolve) => {
    setTimeout(() => resolve("Samosa ๐ŸฅŸ"), 3000);
  });
}

function makeParatha() {
  return new Promise((resolve) => {
    setTimeout(() => resolve("Paratha ๐Ÿซ“"), 1500);
  });
}


Promise.all([makeChai(), makeSamosa(), makeParatha()])
  .then((items) => {
    console.log("Group order complete:", items);
    // Output: ["Regular Chai โ˜•", "Samosa ๐ŸฅŸ", "Paratha ๐Ÿซ“"]
  })
  .catch((error) => {
    console.log("Sorry, we're out of gas! Order failed. โ˜น๏ธ", error);
  });

The key thing here โ€” all three start simultaneously. Promise.all() doesn't wait for chai to finish before starting samosa. Everything runs in parallel!

But if makeSamosa() rejects? The whole .catch() fires. Game over for the group order.

When to use it:

  • You need ALL results before proceeding

  • Example: Fetching user profile + their orders + their settings before showing a dashboard

  • If any one API fails, you don't want to show broken data anyway


Promise.race() โ€” First Chai Ready Wins ๐Ÿƒ

Now imagine Hitesh bhaiya has three helpers today. All three start making chai at the same time. Whoever finishes first โ€” that's the order that gets served.

The student doesn't care which helper made it. They just want chai. Fast.

That's Promise.race().

The first promise to settle (resolve OR reject) wins. Rest are ignored.

function regularChai() {
  return new Promise((resolve) => {
    setTimeout(() => resolve("Regular Chai (3 min) โ˜•"), 3000);
  });
}

function masalaChai() {
  return new Promise((resolve) => {
    setTimeout(() => resolve("Masala Chai (1 min) ๐ŸŒถ๏ธโ˜•"), 1000);
  });
}

function gingerChai() {
  return new Promise((resolve) => {
    setTimeout(() => resolve("Ginger Chai (2 min) ๐Ÿซšโ˜•"), 2000);
  });
}

Promise.race([regularChai(), masalaChai(), gingerChai()])
  .then((firstReady) => {
    console.log("Here's your chai! Others still coming...", firstReady);
    // Output: "Masala Chai (1 min) ๐ŸŒถ๏ธโ˜•" (wins at 1 second)
  })
  .catch((error) => {
    console.log("All failed somehow:", error);
  });

Masala chai finishes first (at 1 second), so that's what you get. The other two are still being made, but you're already sipping away.

When to use it:

  • Timeout logic โ€” if an API doesn't respond in 5 seconds, show an error

  • Fetching from multiple servers โ€” take whoever responds first


Promise.any() โ€” Survival Mode ๐Ÿ†˜

Okay this one is my favorite. Let me paint you a picture.

It's 8:55 AM. Your morning class starts in 5 minutes. You NEED chai. You literally cannot function without it.

Three stalls are near your college. You place orders at all three simultaneously. You just need ANY ONE of them to deliver in time.

That's Promise.any().

The first promise to RESOLVE wins. Rejections are ignored unless ALL fail.

function chaiFromStall1() {
  return new Promise((resolve, reject) => {
    setTimeout(() => reject("Stall 1: Sorry, no milk today ๐Ÿ˜ญ"), 1000);
  });
}

function chaiFromStall2() {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve("Stall 2: Chai ready! โ˜•"), 2000);
  });
}

function chaiFromStall3() {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve("Stall 3: Chai here! โ˜•"), 3000);
  });
}

Promise.any([chaiFromStall1(), chaiFromStall2(), chaiFromStall3()])
  .then((firstSuccess) => {
    console.log("Finally! Zindagi bach gayi โ˜•", firstSuccess);
    // Output: "Stall 2: Chai ready! โ˜•" (first SUCCESS at 2s)
  })
  .catch((error) => {
    // Only fires if ALL three reject
    console.log("ALL stalls failed. No chai for you. ๐Ÿ’€", error);
  });

Stall 1 rejected at 1 second โ€” but Promise.any() doesn't care! It just moves on. Stall 2 resolves at 2 seconds โ€” winner! You get your chai, you make it to class, life is good.

The .catch() only fires when literally every single promise rejects. That's when it throws an AggregateError with all the rejection reasons.

When to use it:

  • Redundancy โ€” trying multiple servers and you just need one to work

  • Fallback APIs โ€” primary fails, try backup

  • Resource loading โ€” try CDN 1, CDN 2, local fallback

Difference from Promise.race(): race() responds to the first settle (reject OR resolve). any() only cares about the first resolve. Small but massive difference. I confused these for way too long. ๐Ÿ˜ญ


Promise.allSettled() โ€” End of Day Report ๐Ÿ“Š

The stall is closing. Hitesh bhaiya wants to know: out of all 20 orders today, how many were fulfilled and how many failed?

He doesn't want to stop early if one order failed. He wants the complete picture โ€” successful orders, failed orders, everything.

That's Promise.allSettled().

Waits for ALL promises to settle (resolve OR reject). Never rejects. Gives you the full report.

function order1() {
  return new Promise((resolve) =>
    setTimeout(() => resolve("Chai delivered to Ravi โœ…"), 1000)
  );
}

function order2() {
  return new Promise((_, reject) =>
    setTimeout(() => reject("Chai spilled for Priya โŒ"), 1500)
  );
}

function order3() {
  return new Promise((resolve) =>
    setTimeout(() => resolve("Chai delivered to Amit โœ…"), 2000)
  );
}

Promise.allSettled([order1(), order2(), order3()])
  .then((results) => {
    results.forEach((result, index) => {
      if (result.status === "fulfilled") {
        console.log(`Order \({index + 1}: โœ… \){result.value}`);
      } else {
        console.log(`Order \({index + 1}: โŒ Failed โ€” \){result.reason}`);
      }
    });
  });

// Output:
// Order 1: โœ… Chai delivered to Ravi โœ…
// Order 2: โŒ Failed โ€” Chai spilled for Priya โŒ
// Order 3: โœ… Chai delivered to Amit โœ…

See that? Even though Order 2 failed, we still got the results for Orders 1 and 3. No early stopping. Full accountability. Hitesh bhaiya knows exactly what happened.

When to use it:

  • Analytics and logging โ€” you need to know what succeeded AND what failed

  • Batch operations โ€” sending 100 emails and you want a full success/fail report

  • Dashboard updates โ€” update as many widgets as possible, report failures separately

This is the one you reach for when you care about every single outcome, not just the happy path.


The Comparison Table ๐Ÿ“‹

Okay let's put it all together. Here's the chai stall cheat sheet:

Method Scenario Resolves When Rejects When
Promise.all() Group order โ€” ALL must be ready ALL resolve ANY one rejects
Promise.race() Speed challenge โ€” first to finish wins FIRST settles (resolve or reject) FIRST settles with reject
Promise.any() Survival mode โ€” just need ONE success FIRST resolves ALL reject
Promise.allSettled() End-of-day report โ€” full picture ALL settle (no rejection possible) Never rejects

Print this. Screenshot it. Tattoo it. Whatever works. ๐Ÿ˜„



About the Author

Saurabh Prajapati is a Full-Stack Software Engineer at IBM India Software Lab, working on Maximo โ€” a cloud-native enterprise solution. I specializes in GenAI, React, and modern web technologies, and loves building things with LangChain, GPT-4, and RAG pipelines.

When he's not writing code, he's probably writing about it.

๐Ÿ“ง saurabhprajapati120@gmail.com | ๐Ÿ™ prajapatisaurabh | ๐Ÿ’ผ saurabh-prajapati

More from this blog

ThitaInfo Blogs

37 posts

Making AI simple, fun, and practical for developers.