JavaScript Event Loop Explained: Call Stack, Microtasks, and Macrotasks
The event loop is the heart of JavaScript concurrency. Understanding call stack, microtask queue for Promises, and macrotask queue for setTimeout is essential for debugging async code.
Every JavaScript developer must understand the event loop. It explains why async code behaves the way it does.
The Call Stack
JavaScript is single-threaded. Functions are pushed onto the call stack, executed, and popped off LIFO.
Microtask Queue (High Priority)
Promise callbacks like then, catch, finally, plus MutationObserver and queueMicrotask. These run BEFORE the next macrotask.
Macrotask Queue (Low Priority)
setTimeout, setInterval, requestAnimationFrame, I/O events. These run one at a time between microtask flushes.
Classic Interview Question
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// Output: 1, 4, 3, 2
Why? 1 and 4 run synchronously. Promise (microtask) runs before setTimeout (macrotask).
Common Gotcha: var in loops
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
// Output: 3, 3, 3 (not 0, 1, 2)
// Fix: use let instead of var
Why This Matters
Understanding the event loop prevents race conditions, explains why UI freezes on heavy computation, and is critical for debugging async flows.
Master JavaScript with our development services.
Written by M Daniyal Amjad Ali
Full Stack Software Engineer with 5+ years of experience. Expert in Next.js, React, Node.js, and Prisma. 100+ projects delivered worldwide.