JavaScript is single-threaded, yet it handles thousands of concurrent operations. The secret is the event loop — a mechanism that continuously checks if there are tasks waiting to execute.
The call stack executes synchronous code line by line. When an async operation completes, its callback is placed in the task queue. The event loop checks: is the call stack empty? If yes, it pushes the next task from the queue onto the stack.
There are two types of queues: the macrotask queue (setTimeout, setInterval, I/O) and the microtask queue (Promises, queueMicrotask). Microtasks always have priority — they execute before the next macrotask. This is why Promise.then() runs before setTimeout() even with 0ms delay.
Understanding the event loop explains common puzzles: why console.log prints before setTimeout with 0ms, why multiple Promises resolve in order, and how requestAnimationFrame synchronizes with the browser repaint cycle.
Tools like Chrome DevTools Performance panel let you visualize the event loop in action, helping you identify bottlenecks and optimize rendering performance.