Explain async/await vs Promises. How does the event loop handle them
async/await is syntax sugar over Promises — same machinery. `await` pauses the async function and schedules its continuation as a microtask when the awaited Promise settles. So `await x` ≈ `.then(continuation)`. The event loop treats both identically: continuations are microtasks, drained fully before the next macrotask.
async/await is syntactic sugar over Promises — not a different mechanism. Understanding that they compile to the same thing is the whole answer.
They're the same machinery
// async/await
async function getUser() {
const res = await fetch("/user");
const data = await res.json();
return data;
}
// roughly equivalent Promise chain
function getUser() {
return fetch("/user").then(res => res.json()).then(data => data);
}An async function always returns a Promise. await unwraps a Promise's resolved value (or throws its rejection).
How the event loop handles them — identically
await x does three things:
- Pauses the async function and pops it off the call stack (the rest of the function becomes a "continuation").
- Lets synchronous code after the call site keep running.
- When
xsettles, schedules the continuation as a microtask.
So await is, to the event loop, exactly a .then callback — a microtask.
async function f() {
console.log("before await");
await null;
console.log("after await"); // <-- this line is a microtask
}
console.log("start");
f();
console.log("end");
// start, before await, end, after awaitWhat async/await gives you over raw Promises
- Readability — sequential-looking code instead of
.thenchains. - try/catch works for async errors (no
.catchchains). - Easier debugging — real stack frames, step-through in devtools.
What raw Promises still do better
- Concurrency —
Promise.all/allSettled/racefor parallel work. A common bug is awaiting in a loop sequentially when the calls are independent:
``js // slow: sequential for (const id of ids) await fetchItem(id); // fast: parallel await Promise.all(ids.map(fetchItem)); ``
- Fire-and-forget chains, or passing a Promise around without blocking.
Senior framing
The senior answer: they're not competing — async/await is the control flow layer, Promises are the value layer. Use await for readable sequential logic, but drop to Promise.all the moment operations are independent. And know that the event loop sees no difference — every await continuation is just a microtask.
Follow-up questions
- •What does an async function return if you don't return anything?
- •How do you run independent awaited calls in parallel?
- •How does error handling differ between .catch and try/catch?
Common mistakes
- •Awaiting independent calls sequentially in a loop instead of Promise.all.
- •Thinking async/await replaces Promises entirely.
- •Forgetting an async function always returns a Promise.
- •Not handling rejections — an un-awaited/un-caught rejected Promise.
Edge cases
- •await on a non-Promise still defers the continuation to a microtask.
- •Throwing inside an async function rejects its returned Promise.
- •Top-level await is allowed in ES modules.
Real-world examples
- •Sequential API calls, parallel data fetching, retry/error handling logic.