JavaScript is a single-threaded language—meaning it processes one operation at a time. But then how does it handle asynchronous behavior like user events, timers, and network requests so efficiently?
The answer lies in the Event Loop, a foundational mechanism that enables JavaScript to handle non-blocking operations smoothly.
Let’s unpack how it works.
🧱 JavaScript Is Single-Threaded (And That’s Okay)
JavaScript executes code using a call stack, processing one task at a time in order. It doesn’t support parallel execution in the traditional multithreading sense. However, thanks to browser-provided features like the Web APIs and the Event Loop, it can still handle asynchronous operations.
These components allow your app to:
- Respond to UI events
- Make API calls without freezing the interface
- Schedule delayed tasks
- And more—all without blocking the main thread
🔁 Event Loop, Call Stack, and Task Queues
To understand the event loop, you first need to understand its core players:
- Call Stack: A stack data structure that tracks function execution. Functions are pushed when called and popped when done.
- Web APIs: Provided by the browser (or Node.js), they handle asynchronous operations like
setTimeout
,fetch
, or DOM events. - Task Queues:
- Macro Task Queue: Holds tasks like
setTimeout
,setInterval
, and UI events. - Micro Task Queue: Holds tasks from
Promise.then()
,MutationObserver
, orqueueMicrotask
.
- Macro Task Queue: Holds tasks like
🔍 How the Event Loop Works
The event loop cycles continuously and follows this order:
- Execute tasks in the call stack until it’s empty.
- Check the microtask queue and process all microtasks.
- If no microtasks remain, process one macro task.
- Repeat from step 1.
This loop ensures the app stays responsive and processes async tasks efficiently.
🧪 Example: Understanding Task Priority

Output:

Why?
A
andD
log immediately via the call stack.Promise.then()
addsC
to the microtask queue.setTimeout(..., 0)
addsB
to the macro task queue.- Event loop finishes the call stack → runs microtasks (
C
) → then runs one macro task (B
).
⚖️ Microtasks vs. Macrotasks
Queue Type | Examples | Priority |
---|---|---|
Microtasks | Promise.then , queueMicrotask , MutationObserver | Higher |
Macrotasks | setTimeout , setInterval , requestAnimationFrame | Lower |
JavaScript will always drain the microtask queue first before executing a single macrotask.
✅ Key Takeaways
- JavaScript is single-threaded but asynchronous capable thanks to the event loop.
- The event loop coordinates between the call stack, microtask queue, and macrotask queue.
- Microtasks are processed before any macrotask in each cycle.
- Understanding the event loop is essential for writing performant, non-blocking frontend code.