Promises, Async/Await & Race Conditions
A Promise is an object representing the eventual completion (or failure) of an asynchronous operation and its resulting value, preventing callback hell via chainable state transitions (pending -> fulfilled/rejected).
Why it matters in interviews
Visual & Interactive Explanation
Promise Combinators Comparison Matrix
Promise.all
Fail-fast parallel execution
- •Maximum concurrency
- •Fails quickly without waiting
- •One failure discards all successful data
Promise.allSettled
Resilient parallel execution
- •No data loss from partial failures
- •Provides status per promise
- •Requires post-processing status array
Promise.race
First settled wins
- •Fastest response
- •Great for timeout boundaries
- •Ignores slower valid responses
Code Examples & Implementation
let currentController: AbortController | null = null;
async function searchUsers(query: string) {
// Cancel the previous pending request
if (currentController) {
currentController.abort();
}
currentController = new AbortController();
try {
const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`, {
signal: currentController.signal,
});
return await res.json();
} catch (err: any) {
if (err.name === 'AbortError') {
console.log('Previous search cancelled cleanly');
return null;
}
throw err;
}
}Interview-Ready Answers
Promises handle async operations cleanly. async/await is syntactic sugar over Promises. Combinators like Promise.all fail fast on first error, while Promise.allSettled waits for all outcomes.
Official Documentation & Specifications
Follow-up Questions & Deep Dives
🎯 Code Output — What Does This Print?
const p = new Promise((resolve, reject) => {
console.log("executor");
resolve("done");
});
p.then((val) => console.log(val));
console.log("after");Promise.resolve(1)
.then((x) => x + 1)
.then((x) => { throw new Error("fail"); })
.then((x) => console.log("success:", x))
.catch((e) => console.log("caught:", e.message))
.then(() => console.log("after catch"));async function bar() {
console.log("bar1");
await new Promise((r) => setTimeout(r, 0));
console.log("bar2");
}
async function foo() {
console.log("foo1");
await bar();
console.log("foo2");
}
foo();
console.log("end");console.log("start");
Promise.all([
Promise.resolve("a"),
Promise.reject("b"),
Promise.resolve("c"),
])
.then((results) => console.log("results:", results))
.catch((err) => console.log("error:", err));
console.log("end");Common Mistakes & Anti-Patterns
- Forgetting to handle .catch() or try/catch resulting in unhandled promise rejections
- Using forEach with async callbacks (it executes concurrently without awaiting)
- Not cancelling obsolete HTTP requests in autocomplete/search inputs
Real-World Architectural Scenario
A user types 'apple' then rapidly changes to 'banana'. The 'apple' request takes 800ms and 'banana' takes 200ms. Without cancellation, 'apple' overwrites 'banana'. How do you resolve this?
Rate Your Readiness
Rate how comfortably you can explain this in an interview to update your global readiness gauge: