JavaScript4 min readUpdated Jul 1, 2026Technical Concept

Event Loop Explained With Examples

Understand how the JavaScript event loop runs synchronous code, microtasks, and macrotasks with practical interview examples.

JavascriptNode.jsBrowser ApisJavascriptEvent Loop

Direct definition

The JavaScript event loop decides what runs next after the current call stack is empty. It coordinates synchronous code, runtime APIs, microtasks such as promises, and macrotasks such as timers and events.

Small Runnable Code Example

JavaScriptCode / Output / Explanation
console.log("Start");

setTimeout(() => {
  console.log("Timeout");
}, 0);

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

console.log("End");

Expected output

Start
End
Promise
Timeout

Synchronous logs run first. The promise callback is a microtask, so it runs before the timer macrotask.

Visual Event Loop Diagram

Flow diagram

Synchronous work enters the call stack. Runtime APIs schedule callbacks into queues. Microtasks run before the next macrotask.

flowchart LR
    A[Call Stack] --> B[Web APIs / Runtime APIs]
    B --> C[Microtask Queue]
    B --> D[Macrotask Queue]
    C --> E[Event Loop]
    D --> E
    E --> A

How JavaScript Executes This Code

Step 1

console.log("Start") runs on the call stack.

Step 2

setTimeout schedules its callback in the macrotask queue.

Step 3

Promise.then schedules its callback in the microtask queue.

Step 4

console.log("End") runs before queued callbacks.

Step 5

The call stack becomes empty.

Step 6

The microtask queue runs, printing "Promise".

Step 7

The macrotask queue runs next, printing "Timeout".

Internal Components

Call stack

Runs synchronous JavaScript one frame at a time.

Web APIs / Runtime APIs

Handle timers, events, network work, and platform callbacks outside the stack.

Microtask queue

Holds Promise callbacks and runs before the next macrotask.

Macrotask queue

Holds timers, events, intervals, and other scheduled tasks.

Common Event Loop Problems

Blocking the main thread

Long synchronous loops prevent timers, rendering, and input handling from running. Break heavy work into chunks or move it to a worker.

Delayed setTimeout

setTimeout(fn, 0) means run after the current stack and microtasks, not immediately.

Microtask starvation

Repeatedly scheduling promises can delay macrotasks and make the page feel stuck.

Callback hell

Deep nested callbacks make execution order hard to reason about. Prefer promises or async/await for clearer sequencing.

Best Practices

  • Keep synchronous work short.
  • Use async/await for readable async flows.
  • Understand that promises run before timers.
  • Use workers for CPU-heavy browser tasks.

Quiz

What runs first after the synchronous call stack becomes empty?

A. Macrotask queue
B. Microtask queue
C. setTimeout queue always
D. Browser rendering always

Correct answer: microtasks run after the stack clears and before the next macrotask.