A Horror Story About JavaScript Promise
Source: Dev.to
Before the Midnight Adventure – What’s a Promise?
“Don’t worry – it’s easier than facing a monster under the bed!”
A Promise is an object that represents the eventual completion (or failure) of an asynchronous operation.
Real‑life analogy:
Your friend promises to bring you coffee. Right now you don’t have it – the promise is pending. Later you either get the coffee (fulfilled) or your friend calls to say they spilled it (rejected).
In JavaScript a Promise is created with the new Promise() constructor, which receives a function with two parameters: resolve and reject.
const coffeePromise = new Promise((resolve, reject) => {
// Simulate making coffee
const success = true;
if (success) {
resolve("☕ Here's your coffee!");
} else {
reject("💔 Spilled the coffee...");
}
});
Handling the result
.then()runs when the Promise is fulfilled and receives the resolved value..catch()runs when the Promise is rejected and receives the error.
coffeePromise
.then(message => {
console.log(message); // "☕ Here's your coffee!"
})
.catch(error => {
console.log(error); // "💔 Spilled the coffee..."
});
You can chain multiple .then() calls, but we’ll keep it simple for now.
The 3:34 AM Bathroom Trip – A Promise Analogy
It’s 3:34 AM. Your bladder is screaming like a DDoS attack. You need to get to the bathroom, but:
- You live alone.
- Grandmother’s stories say ghosts roam after 3 AM.
- Monsters under the bed are most active.
- The hallway feels like a road to hell.
You’re frozen, weighing two options:
- Wet the bed
- Face the darkness (supernatural beings)
This is exactly like handling multiple promises in JavaScript.
The three conditions you need
- Flip the hallway switch – hope it works.
- Flip the bathroom light – hope it works.
- Check under the bed – hope there’s no monster salivating at you.
// Hallway light
const hallwayLight = new Promise((resolve, reject) => {
const bulbWorks = Math.random() > 0.3; // 70 % chance it works
setTimeout(() => {
if (bulbWorks) {
resolve("💡 Hallway light is ON");
} else {
reject("💀 Hallway light bulb is dead");
}
}, 1000);
});
// Bathroom light
const bathroomLight = new Promise((resolve, reject) => {
const bulbWorks = Math.random() > 0.2; // 80 % chance it works
setTimeout(() => {
if (bulbWorks) {
resolve("🚽 Bathroom light is ON");
} else {
reject("👻 Bathroom light is dead");
}
}, 1500);
});
// Under‑bed check
const underBedCheck = new Promise((resolve, reject) => {
const monsterPresent = Math.random() > 0.6; // 40 % chance of monster
setTimeout(() => {
if (!monsterPresent) {
resolve("🛏️ Under the bed is CLEAR");
} else {
reject("👹 MONSTER under the bed!");
}
}, 2000);
});
1️⃣ Promise.all()
“I will ONLY get out of bed if ALL THREE things happen. If any fail, I stay right here.”
Promise.all([hallwayLight, bathroomLight, underBedCheck])
.then(results => {
console.log("🎉 All systems go! Time to sprint!");
console.log(results);
// You get out of bed and run with tears of happiness
})
.catch(error => {
console.log("😭 NOPE. Staying in bed.");
console.log("Reason:", error);
// You wet the bed. Game over.
});
Possible output
Success
🎉 All systems go! Time to sprint!
['💡 Hallway light is ON', '🚽 Bathroom light is ON', '🛏️ Under the bed is CLEAR']
Failure
😭 NOPE. Staying in bed.
Reason: 💀 Hallway light bulb is dead
When to use Promise.all()
- All API calls must succeed before rendering a page.
- Uploading multiple files where every file must finish.
2️⃣ Promise.allSettled()
“Let me check everything first. I’ll wait until I know the status of all three safety measures, then decide based on reality.”
Promise.allSettled([hallwayLight, bathroomLight, underBedCheck])
.then(results => {
console.log("📋 Let's assess the situation:");
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`✅ Promise ${index + 1}:`, result.value);
} else {
console.log(`❌ Promise ${index + 1}:`, result.reason);
}
});
// Make a decision based on the collected information
});
Sample output
📋 Let's assess the situation:
❌ Promise 1: 💀 Hallway light bulb is dead
✅ Promise 2: 🚽 Bathroom light is ON
✅ Promise 3: 🛏️ Under the bed is CLEAR
When to use Promise.allSettled()
- You need the result of every promise, regardless of success or failure.
- Logging/reporting the outcome of a batch of operations.
3️⃣ Promise.race() (optional – not in original text but often paired)
“Whichever safety measure resolves first decides my fate.”
Promise.race([hallwayLight, bathroomLight, underBedCheck])
.then(first => {
console.log("🏁 First result:", first);
})
.catch(error => {
console.log("💥 First error:", error);
});
When to use Promise.race()
- Implementing timeouts.
- Acting on the fastest response among several sources.
4️⃣ Promise.any() (optional – not in original text but completes the set)
“I’ll go as soon as any safety measure succeeds; failures are ignored until one works.”
Promise.any([hallwayLight, bathroomLight, underBedCheck])
.then(firstSuccess => {
console.log("🚀 First successful promise:", firstSuccess);
})
.catch(aggregateError => {
console.log("❗ All promises rejected:", aggregateError);
});
When to use Promise.any()
- Trying multiple redundant services and proceeding with the first that works.
- Fallback strategies where any success is sufficient.
Takeaway
Promise.all→ All must succeed.Promise.allSettled→ Know the outcome of every promise.Promise.race→ First settled (fulfilled or rejected) wins.Promise.any→ First fulfilled wins; ignore rejections until one succeeds.
Armed with these tools, you can survive any 3 AM bathroom trip—or any asynchronous JavaScript challenge! 🚀
Promise Methods Illustrated with a 3 AM Horror Story
Below is a cleaned‑up version of the original markdown. The structure, emojis, and narrative are preserved, but the formatting now follows proper Markdown conventions.
1️⃣ Promise.all()
Promise.all([hallwayLight, bathroomLight, underBedCheck])
.then((results) => {
console.log('✅ All checks completed:');
results.forEach((result, i) => console.log(` ${i + 1}. ${result}`));
})
.catch((error) => {
console.error('❌ One of the checks failed:', error);
});
When to use
- You need all operations to succeed before proceeding.
- Example: uploading several files and you only continue if every upload succeeds.
- Example: a batch job that must finish completely before moving on.
2️⃣ Promise.allSettled()
Promise.allSettled([hallwayLight, bathroomLight, underBedCheck])
.then((results) => {
console.log('📋 Full report of every operation:');
results.forEach((result, i) => {
if (result.status === 'fulfilled') {
console.log(` ${i + 1}. ✅ ${result.value}`);
} else {
console.log(` ${i + 1}. ❌ ${result.reason}`);
}
});
const anyLight = results.some(r =>
r.status === 'fulfilled' &&
(r.value.includes('light') || r.value.includes('CLEAR'))
);
if (anyLight) {
console.log('🏃 Good enough! Making a run for it!');
} else {
console.log('😱 Complete darkness AND monsters? Staying put.');
}
});
When to use
- You need the outcome of every promise, regardless of success or failure.
- Typical scenarios:
- Uploading multiple files and you want to know which succeeded/failed.
- Running a batch process and you need a complete report.
3️⃣ Promise.race()
Promise.race([hallwayLight, bathroomLight, underBedCheck])
.then((firstResult) => {
console.log(`🏁 First thing happened: ${firstResult}`);
console.log('🚀 Taking that as a sign! Running NOW!');
// You sprint regardless of what the first result was
})
.catch((firstError) => {
console.log(`🏁 First thing happened (and it was bad): ${firstError}`);
console.log('😰 Oh no. That’s a bad sign. But I’m already out of bed!');
// You’re already committed. You run into darkness.
});
When to use
- You need the first settled promise (fulfilled or rejected).
- Common uses:
- Implementing timeouts:
Promise.race([actualRequest, timeoutPromise]). - Getting the fastest response from multiple sources.
- Implementing timeouts:
4️⃣ Promise.any()
Promise.any([hallwayLight, bathroomLight, underBedCheck])
.then((firstSuccess) => {
console.log(`🎯 Found one working safety measure: ${firstSuccess}`);
console.log('🏃 That’s enough! Making my move!');
})
.catch((error) => {
console.log('💀 ALL safety measures failed!');
console.log('Errors:', error.errors); // AggregateError
console.log('Staying in bed forever.');
});
When to use
- You only need one successful result; the rest can be ignored.
- Typical scenarios:
- Fallback APIs (try server A, if it fails try server B).
- Any situation where a single success is sufficient to proceed.
Quick Recap
| Method | What it Returns | When to Choose |
|---|---|---|
Promise.all() | All fulfilled values (or rejects on first failure) | You need everything to succeed. |
Promise.allSettled() | Array of {status, value/reason} for every promise | You want a full report, successes and failures. |
Promise.race() | First settled value or rejection | You care about speed, not completeness. |
Promise.any() | First fulfilled value (or AggregateError if none) | Any single success is enough. |
🎮 Play the Pixel‑Art Game!
If you enjoyed this story, check out the pixel‑art game I built to go with it. Click the screenshot below, pick your Promise method, and try to survive the night!
Feedback welcome!
If you spot any mistakes or have suggestions, let me know. You can find me on LinkedIn and X where I share more fun code snippets.