Explain event loop and how asynchronous tasks are executed in JavaScript.
Duplicate-style question on the event loop and async execution: JS hands async work to Web APIs, which queue callbacks (microtask for Promises, macrotask for timers/events) on completion; the event loop drains all microtasks then runs one macrotask whenever the call stack is empty.
(This is the same core topic as the other event-loop questions — here's the focused version on how async tasks get executed.)
The flow
- You call an async API.
setTimeout,fetch,addEventListener— these are Web APIs provided by the browser, not the JS engine. JS registers them and immediately returns; the stack keeps going. - The host does the work off-thread. The browser runs the timer, performs the network request, watches for the event. The JS thread is free.
- On completion, the callback is queued.
- Promise reactions /
awaitcontinuations → microtask queue. - Timers, events, I/O → macrotask (task) queue.
- The event loop schedules it. When the call stack is empty: drain all microtasks → (maybe render) → run one macrotask → repeat.
Code trace
console.log("1");
setTimeout(() => console.log("2"), 0); // browser timer -> macrotask
fetch("/x").then(() => console.log("3"));// browser network -> microtask on resolve
console.log("4");
// 1, 4, then 3 (when response arrives, as a microtask) ... 2 may print before 3
// if the network is slow — ordering between an I/O macrotask source and a
// pending request depends on when the request actually completes.(For a guaranteed-resolved Promise vs setTimeout, the Promise always wins. For a real fetch, it depends on network timing — but the callback, once queued, is still a microtask.)
Key takeaways
- JS itself is synchronous and single-threaded; concurrency comes from the host + the queues.
- The callback only runs when the stack is empty — a long sync block delays every pending async callback.
- Microtasks outrank macrotasks every loop iteration.
Senior framing
Frame it as delegation + scheduling: the engine delegates async work to the environment and the event loop schedules the results back onto the single thread, prioritizing microtasks. That model explains both responsiveness and jank.
Follow-up questions
- •If the main thread is blocked, when does a completed fetch callback run?
- •Why are timers macrotasks but Promise resolutions microtasks?
- •What runs the actual network request?
Common mistakes
- •Believing the JS engine performs the network/timer work itself.
- •Assuming async = parallel JS.
- •Ignoring microtask vs macrotask priority.
Edge cases
- •A blocked main thread queues callbacks but runs none until it unblocks.
- •fetch resolution timing depends on the network; the queued callback is still a microtask.
Real-world examples
- •fetch, event listeners, setTimeout, requestIdleCallback.