Event Loop
Event loop = JavaScript's async mechanism
What is the Event Loop?
"Single thread + async callbacks"
The event loop is JavaScript's mechanism for handling asynchronous operations in a single-threaded environment.
JavaScript Runtime Components
Call Stack · Web APIs · Callback Queue · Event Loop
┌─────────────┐
│ Call Stack │ ← Synchronous code
└─────────────┘
↓
┌─────────────┐
│ Web APIs │ ← setTimeout, fetch, DOM
└─────────────┘
↓
┌─────────────┐
│ Callback │ ← Ready callbacks
│ Queue │
└─────────────┘
↓
┌─────────────┐
│ Event Loop │ ← Moves callbacks to stack
└─────────────┘
"The event loop coordinates the call stack, Web APIs, and callback queue to handle asynchronous operations."
How It Works
Stack → APIs → Queue → Loop
- Call Stack: Executes synchronous code
- Web APIs: Handle async operations (setTimeout, fetch)
- Callback Queue: Holds ready callbacks
- Event Loop: Moves callbacks from queue to stack when stack is empty
"The event loop continuously checks if the call stack is empty, then moves callbacks from the queue to the stack."
Example Flow
Synchronous first, async later
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
// Output: 1, 4, 3, 2
Why?
1and4are synchronous3is microtask (Promise)2is macrotask (setTimeout)
"Synchronous code runs first, then microtasks (Promises), then macrotasks (setTimeout)."
Microtasks vs Macrotasks
Microtasks > Macrotasks
| Type | Examples | Priority |
|---|---|---|
| Microtasks | Promises, queueMicrotask | High |
| Macrotasks | setTimeout, setInterval | Low |
"Microtasks (Promises) have higher priority than macrotasks (setTimeout) in the event loop."
Call Stack
LIFO - Last In, First Out
- Executes code synchronously
- One function at a time
- Blocks when busy
- Stack overflow if too deep
"The call stack executes code synchronously in a LIFO manner, blocking when processing."
Blocking the Event Loop
Heavy computation blocks everything
// ❌ Blocks event loop
for (let i = 0; i < 1000000000; i++) {
// heavy computation
}
// ✅ Use Web Workers in React for heavy tasks (e.g. image processing)
function App() {
useEffect(() => {
if (window.Worker) {
const worker = new Worker("worker.js");
worker.postMessage("start");
worker.onmessage = (e) => {
console.log("Worker result:", e.data);
};
return () => worker.terminate();
}
}, []);
return <div>Web Worker Example in React</div>;
}
## Other Ways to Avoid Blocking the Event Loop
> **Break up heavy work into chunks**
### 1. Use `setTimeout` or `setImmediate` to Chunk Work
Split long-running tasks into smaller pieces and schedule them so the event loop stays responsive:
```javascript
function processArray(arr) {
function processChunk(start) {
const chunkSize = 1000;
for (let i = start; i < Math.min(start + chunkSize, arr.length); i++) {
// Process item arr[i]
}
if (start + chunkSize < arr.length) {
setTimeout(() => processChunk(start + chunkSize), 0); // Schedule next chunk
}
}
processChunk(0);
}
// This keeps the UI responsive!
2. requestIdleCallback (Browser)
Schedule non-urgent work when the browser is idle:
function doHeavyWork(deadline) {
while (deadline.timeRemaining() > 0 && tasks.length > 0) {
// Process one or more tasks
tasks.shift();
}
if (tasks.length > 0) {
requestIdleCallback(doHeavyWork);
}
}
requestIdleCallback(doHeavyWork);
3. setImmediate (Node.js Only)
setImmediate(() => {
// run this after I/O, before timers
});
Summary:
Use Web Workers, chunk work with timers, or userequestIdleCallback(browser) /setImmediate(Node) to avoid blocking the event loop.
Even simple breaks likesetTimeout(fn, 0)let the event loop breathe.
> "Heavy synchronous operations block the event loop, preventing other code from running."
---
## setTimeout(0)
> **Defer to next event loop cycle**
```javascript
console.log("1");
setTimeout(() => console.log("2"), 0);
console.log("3");
// Output: 1, 3, 2
Even with 0ms delay, setTimeout runs after current execution.
"setTimeout with 0ms defers execution to the next event loop cycle, after current code completes."
Promise Queue
Promises = microtask queue
Promise.resolve().then(() => console.log("microtask"));
setTimeout(() => console.log("macrotask"), 0);
// microtask runs first
"Promises are added to the microtask queue, which has priority over the macrotask queue."
Visual Example
Order matters
console.log("Start");
setTimeout(() => console.log("Timeout"), 0);
Promise.resolve()
.then(() => console.log("Promise 1"))
.then(() => console.log("Promise 2"));
console.log("End");
// Output: Start, End, Promise 1, Promise 2, Timeout
🧠 Ultra-Short Cheat Sheet
Single-threaded
Call stack (synchronous)
Web APIs (async)
Callback queue
Event loop (coordinator)
Microtasks > Macrotasks
Synchronous first