⏳ JavaScript Promises and Async/Await — Asynchronous Code Made Simple
Master JavaScript Promises and async/await. Understand the event loop, .then chaining, error handling, Promise.all/any/race and the interview questions that trip people up.
JavaScript is single-threaded — but it doesn't wait. When you call fetch(), the engine doesn't freeze; it kicks off the request and keeps going. Promises and async/await are how you handle the result.
🧠 A Promise is a placeholder
A Promise represents a future value with three states:
pending— still workingfulfilled— done, here's the valuerejected— failed, here's the error
🎯 async/await is just sugar
// Promise style
fetch('/api/user')
.then(r => r.json())
.then(user => console.log(user))
.catch(err => console.error(err));
// async/await style
async function load() {
try {
const r = await fetch('/api/user');
const user = await r.json();
console.log(user);
} catch (err) {
console.error(err);
}
}⚡ Useful combinators
Promise.all([p1, p2])— wait for ALL, fail if ANY failsPromise.allSettled(...)— wait for all, never failsPromise.any(...)— first to fulfill winsPromise.race(...)— first to settle (fulfilled OR rejected) wins
🔁 The event loop in one line
Synchronous code → microtasks (Promises) → macrotasks (setTimeout, I/O). Microtasks always finish before the next macrotask.
💻 Code Examples
Parallel requests with Promise.all
async function loadAll() {
const [users, posts] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
]);
return { users, posts };
}Output:
Both requests fire at the same time — much faster than awaiting one at a time.
Microtask vs Macrotask order
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');Output:
A D C B
Sequential vs parallel — common bug
// ❌ Sequential — slow
const u = await fetchUser();
const p = await fetchPosts();
// ✅ Parallel — fast
const [u, p] = await Promise.all([fetchUser(), fetchPosts()]);Output:
Parallel is 2x faster when calls are independent.
⚠️ Common Mistakes
- Forgetting `await` — you get a Promise object instead of the resolved value.
- Using await in a loop when calls are independent — sequential when it could be parallel.
- Not handling rejection — unhandled Promise rejections crash Node and warn in the browser.
- Mixing .then() and await unnecessarily — pick one style per function.
🎯 Interview Questions
Real questions asked at top product and service-based companies.
Q1.What's the difference between sync and async code?Beginner
Sync code runs top-to-bottom, blocking the thread until each line finishes. Async code starts an operation, returns immediately, and the result arrives later via a callback, Promise or await.
Q2.What's the difference between Promise.all and Promise.allSettled?Intermediate
Promise.all rejects as soon as ANY promise rejects (others are ignored). Promise.allSettled always waits for all, giving you an array of { status, value/reason } results.
Q3.Why do microtasks run before macrotasks?Intermediate
The event loop drains the entire microtask queue (Promise callbacks, queueMicrotask) after every macrotask. This guarantees promise chains run together and finish before the next setTimeout or I/O callback.
Q4.What happens when you `await` a non-Promise value?Advanced
It's wrapped in `Promise.resolve(value)` and awaited. So `await 5` just returns 5 after a microtask tick — useful for normalizing return types.
Q5.How would you implement Promise.all from scratch?Advanced
function all(promises) {
return new Promise((resolve, reject) => {
const out = []; let done = 0;
promises.forEach((p, i) => {
Promise.resolve(p).then(v => {
out[i] = v;
if (++done === promises.length) resolve(out);
}, reject);
});
});
}
🧠 Quick Summary
- Promise = future value in pending/fulfilled/rejected state.
- async/await is syntactic sugar over .then() / .catch().
- Promise.all for parallel, allSettled to never fail, any for first success, race for first to settle.
- Microtasks (Promises) run before macrotasks (setTimeout).
- Always handle rejection — try/catch around await, or .catch() at the end.