RMRM Full Stack & AI Engineer · All guides · Roadmaps
Languages · guide

The JavaScript Event Loop

The JavaScript Event Loop is the mechanism that allows a single-threaded language to handle asynchronous operations — like network requests, timers, and user input — without blocking the main thread. Understanding it is essential for writing performant, bug-free JavaScript.

What Is the Event Loop?

JavaScript runs on a single thread, meaning it can only execute one piece of code at a time. The event loop is a continuous process built into the JS runtime (like V8 in Chrome or Node.js) that coordinates the call stack, the task queue, and the microtask queue. Its job is simple: if the call stack is empty, pick the next task and push it onto the stack for execution.

The Call Stack

The call stack is a LIFO (last-in, first-out) data structure that tracks which function is currently executing. When a function is invoked it is pushed onto the stack, and when it returns it is popped off. If synchronous code runs for too long without returning, it blocks the stack and freezes the UI — this is known as 'blocking the main thread.'

The Task Queue and Microtask Queue

Asynchronous callbacks (e.g., setTimeout, setInterval, I/O events) are placed in the Task Queue (also called the Macrotask Queue) once their triggering condition is met. Promises and queueMicrotask callbacks go into the Microtask Queue instead. The event loop always drains the entire Microtask Queue after each task before moving on to the next macrotask, giving microtasks higher priority.

How It All Fits Together

The event loop follows this cycle: execute the current synchronous script, then drain all microtasks, then pick one macrotask from the task queue, then drain microtasks again, and repeat. This means a resolved Promise (.then callback) will always run before a setTimeout with a 0ms delay, even though both appear 'asynchronous.' Visualizing this cycle is the key to predicting execution order.

Key Gotcha: Zero-Delay Timers Are Not Instant

setTimeout(fn, 0) does not execute fn immediately — it schedules fn as a macrotask to run after the current call stack and all pending microtasks are cleared. If the call stack is busy or microtasks keep queuing themselves, the timer callback can be delayed significantly beyond 0ms. Never rely on setTimeout(fn, 0) for precise timing or as a substitute for microtasks.

Best Practice: Keep the Call Stack Free

Long-running synchronous operations (heavy loops, large JSON parsing) block the event loop and make your app unresponsive. Break heavy work into smaller chunks using setTimeout or requestAnimationFrame, or offload it to a Web Worker so the main thread remains free to handle user interactions and render updates. Modern APIs like requestIdleCallback also let you schedule non-critical work during browser idle periods.

Go deeper with an AI tutor that teaches this in context — and quizzes you on it.
Open the app — free to start

© RM Full Stack & AI Engineer · All guides · Roadmaps · Open the app