Event Loop β How JavaScript Handles Asynchronicity
After finishing this topic
You will understand the principle of how JavaScript can handle asynchronicity while being single-threaded.
How can it be single-threaded?
JavaScript executes only one task at a time. It's single-threaded. However, it handles timers, network requests, and click events concurrently. How?
The secret lies in the Event Loop. The JavaScript engine (V8) doesn't do it alone. The browser (or Node.js) helps.
Three Components
Call Stack: This is where currently executing functions are stacked. When a function is called, it's added to the stack, and when its execution finishes, it's removed. It follows a LIFO (Last In, First Out) structure.
Web API: These are features provided by the browser. They include setTimeout, fetch, and DOM event listeners. They operate outside the JavaScript engine.
Task Queue: This is the queue where callbacks completed by the Web API wait. When the call stack is empty, one by one, they are taken out from here and executed.
Execution Flow
console.log("1");
setTimeout(() => {
console.log("2");
}, 0);
console.log("3");Output: 1, 3, 2.
Even though it's a 0ms timer, "2" is printed last. Following the flow:
console.log("1")β Executed in the call stack β OutputsetTimeoutβ Registers a timer in the Web API β Removed from the call stackconsole.log("3")β Executed in the call stack β Output- The call stack is empty β The event loop checks the task queue β Executes the callback β Prints "2"
setTimeout(fn, 0) means "execute when the call stack is empty," not "execute immediately."
Microtask Queue
The .then() of a Promise goes into the Microtask Queue, not the task queue. Microtasks are executed before tasks.
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");Output: 1, 4, 3, 2.
When the call stack is empty, the event loop:
- First, empties the entire microtask queue (Promise callbacks)
- Then, takes one from the task queue (setTimeout callback)
async/await also uses Promises internally, so it's a microtask.
If the Call Stack is Occupied for a Long Time
while (true) {
// Infinite loop
}If the call stack is not empty, the event loop will not run. Clicks will not respond, and timers will not execute. The browser will display "This page is not responding."
If you need to perform heavy calculations, use Web Worker or break down the work so that the call stack is cleared periodically.
Key Points
JavaScript is single-threaded, but it handles asynchronicity using the browser's Web API and the Event Loop.
setTimeout(fn, 0)means "execute when the call stack is empty." Microtasks (Promises) are always executed before tasks (setTimeout).