To understand how JavaScript handles asynchronous operations, it's crucial to dive into the Call Stack, Task Queue, Web APIs, and Microtask Queue, which form the foundation of the JavaScript runtime environment.
1. Call Stack
The Call Stack is a data structure used by the JavaScript engine to keep track of the function calls during program execution.
How It Works:
Push Operations: When a function is invoked, it is added to the top of the stack.
Pop Operations: When a function completes its execution, it is removed from the stack.
Example:
function first() {
console.log("First");
}
function second() {
console.log("Second");
first();
}
second();
Execution Flow:
second()
is added to the stack.Inside
second()
,console.log("Second")
executes.first()
is called, so it is added to the stack.console.log("First")
insidefirst()
executes.first()
finishes and is removed from the stack.second()
finishes and is removed.
Visual Representation:
Call Stack:
- second()
- first()
2. Web APIs
Web APIs are provided by the browser or environment (like Node.js) to handle asynchronous tasks, such as timers, network requests, or event listeners.
Key Features:
Web APIs operate outside the JavaScript engine.
Examples include:
setTimeout
for timers.fetch
for HTTP requests.Event Listeners for user interactions.
Example:
console.log("Start");
setTimeout(() => {
console.log("Async Task");
}, 1000);
console.log("End");
Execution Flow:
console.log("Start")
executes (synchronous).setTimeout
is called, and the timer is set in the Web API.console.log("End")
executes (synchronous).After 1 second, the callback for
setTimeout
is moved to the Task Queue.
3. Task Queue
The Task Queue (or Callback Queue) is where asynchronous tasks, such as those initiated by setTimeout
or fetch
, wait until the Call Stack is empty before being executed.
How It Works:
When a Web API operation completes (e.g.,
setTimeout
timer expires), its callback is placed in the Task Queue.The Event Loop continuously checks if the Call Stack is empty.
If the Call Stack is empty, the Event Loop moves the first task from the Task Queue to the Call Stack.
Example:
console.log("Start");
setTimeout(() => {
console.log("Task Queue Callback");
}, 0);
console.log("End");
Execution Flow:
console.log("Start")
runs.setTimeout
schedules the callback in the Web API (with 0ms delay).console.log("End")
runs.The Call Stack is now empty, so the Event Loop moves the
setTimeout
callback to the Call Stack.console.log("Task Queue Callback")
runs.
Output:
Start
End
Task Queue Callback
4. Microtask Queue
The Microtask Queue (or Job Queue) is a specialized queue for high-priority tasks. Promises and async/await
callbacks are added to this queue. The Microtask Queue is always processed before the Task Queue.
Key Features:
Microtasks have a higher priority than tasks in the Task Queue.
Promises,
async/await
, andqueueMicrotask
operations are enqueued here.
Example:
console.log("Start");
setTimeout(() => {
console.log("Task Queue Callback");
}, 0);
Promise.resolve().then(() => {
console.log("Microtask Callback");
});
console.log("End");
Execution Flow:
console.log("Start")
runs.setTimeout
schedules its callback in the Web API.Promise.resolve().then(...)
schedules a callback in the Microtask Queue.console.log("End")
runs.The Event Loop processes the Microtask Queue first, so
Promise
's callback runs.Then the Event Loop processes the Task Queue, so the
setTimeout
callback runs.
Output:
Start
End
Microtask Callback
Task Queue Callback
Interaction Between Components
Event Loop in Action
The Event Loop orchestrates the interaction between the Call Stack, Task Queue, and Microtask Queue.
Executes synchronous code on the Call Stack.
Processes Microtasks from the Microtask Queue after the Call Stack is empty.
Processes tasks from the Task Queue only when both the Call Stack and Microtask Queue are empty.
Example:
console.log("Start");
setTimeout(() => {
console.log("Task Queue");
}, 0);
Promise.resolve().then(() => {
console.log("Microtask Queue");
});
console.log("End");
Execution Order:
console.log("Start")
runs.setTimeout
schedules a callback in the Task Queue.Promise.resolve().then(...)
schedules a callback in the Microtask Queue.console.log("End")
runs.The Event Loop processes the Microtask Queue, so
Promise
's callback runs.Finally, the Event Loop processes the Task Queue, so
setTimeout
's callback runs.
Output:
Start
End
Microtask Queue
Task Queue
Comparison of Queues
Component | Priority | Used For | Examples |
Call Stack | Highest | Executes synchronous code | Function calls |
Microtask Queue | Higher than Task | Promises, async/await , queueMicrotask | Promise.then |
Task Queue | Lower than Micro | Asynchronous callbacks, timers, events | setTimeout , fetch |
Summary
Call Stack: Executes all synchronous code.
Web APIs: Manage asynchronous operations (e.g., timers, HTTP requests).
Task Queue: Holds asynchronous callbacks like
setTimeout
and processes them after Microtasks.Microtask Queue: A high-priority queue for Promises and
async/await
.