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. ๐
Resources & Links
๐ MDN Docs: Promise
๐ MDN: Promise.all()
๐ MDN: Promise.race()
๐ MDN: Promise.any()
๐ GitHub: prajapatisaurabh
๐ผ LinkedIn: saurabh-prajapati
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


