JavaScript Event Loop

Introduction

JavaScript is single-threaded — it can only run one line of code at a time. So how does it handle async operations, timers, and network requests without freezing? The answer is the Event Loop. It decides what code runs now, what waits, and what gets executed later.

1. Call Stack

The place where JavaScript executes code line by line.

function hello() {
  console.log("Hello");
}

hello();
    

Each function call goes onto the stack.

2. Web APIs (Browser Background Threads)

Timers, fetch requests, DOM events, etc., run outside the call stack.

Example:

console.log("Start");

setTimeout(() => {
  console.log("Inside timeout");
}, 2000);

console.log("End");
    

Output:

Start
End
Inside timeout
    

Even with 0ms delay, setTimeout never runs immediately.

3. Callback Queue (Task Queue)

When async tasks finish (like setTimeout), the callback is placed in the queue, waiting for the call stack to be empty.

4. Microtask Queue

Higher priority queue. Promises go here.

console.log("A");

Promise.resolve().then(() => {
  console.log("B");
});

console.log("C");
    

Output:

A
C
B
    

Microtasks (Promises) run before normal callbacks.

5. Full Example: Event Loop Priority

console.log("1");

setTimeout(() => console.log("2"));

Promise.resolve().then(() => console.log("3"));

console.log("4");
    

Output:

1
4
3
2
    

6. Why This Matters

7. async/await & Event Loop

async function run() {
  console.log("A");
  await Promise.resolve();
  console.log("B");
}

run();
console.log("C");
    

Output:

A
C
B
    

8. Long Blocking Operations Freeze Everything

while (true) {} // freezes browser
    

Because the call stack never clears — event loop can't run queued tasks.

9. Offloading Heavy Work to Web Workers

// worker.js
onmessage = () => {
  let sum = 0;
  for (let i = 0; i < 1e9; i++) sum++;
  postMessage(sum);
};
    

Workers run on a separate thread.

Summary