Execution Model
// Global context (global execution context)
console.log("Start of script");
var x = "Global Execution Context";
Promise.resolve().then(() => {
console.log("Inside first Promise then (microtask)");
});
setTimeout(() => {
console.log("Inside setTimeout (macrotask)");
Promise.resolve().then(() => {
console.log("Inside Promise then (microtask)");
});
}, 1000);
Promise.resolve().then(() => {
console.log("Inside last Promise then (microtask)");
});
function foo() {
let bar = "Hello from foo";
console.log(bar); // Accessing local variable bar
}
foo();
console.log("End of script");
Visualize Execution: https://www.jsv9000.app/
Global Execution Context and Call Stack
- Call Stack: The first thing that happens when the script runs is that the global execution context is pushed onto the call stack. This is the top-level execution context for the whole script.
- Global Scope: The global context holds all the global variables and functions.
Execution Starts
- The global context starts execution and begins with
console.log('Start of script')and .... - The call stack executes this log, and it’s popped off the stack once executed.
- Then, the global execution context proceeds to define the variable
xwith the value"Global Execution Context", but nothing is printed for that as it's just an assignment.
Asynchronous Task
setTimeoutandPromise.resolve().then(...)are asynchronous functions.- When the JavaScript engine encounters
setTimeout, it doesn't execute it immediately. Instead, it sends it to the Web API environment (which is part of the browser's JavaScript runtime).setTimeoutsets a timer for 1000 ms and doesn't block the execution.- The
Promise.resolve().then(...)is also handed over to the Web API but for asynchronous execution. However, they are added to the microtask queue to be executed before macrotasks, ensuring that microtasks are executed first.
Local Execution Context
- The function
foo()is called, so a new stack frame is pushed onto the call stack. - Inside
foo(), the variablebaris declared and logged:let bar = 'Hello from foo'creates a local variable in thefoofunction's scope.- The call stack handles this code and executes it.
- Once
foo()finishes executing, it is popped from the call stack, returning control to the global execution context.
Logging the End of Script (Call Stack)
console.log('End of script')is executed synchronously.- This is logged, and the function call is popped from the call stack.
Event Loop: Handling setTimeout (Macrotask Queue)
-
Event Loop: Once all synchronous code finishes (i.e., the call stack is empty), the event loop checks if there are any tasks in the macrotask queue (also known as the task queue).
-
Macrotask Queue: The callback from
setTimeout(after 1000 ms) is now ready to be executed, so it is moved to the macrotask queue. -
The event loop moves this task from the macrotask queue to the call stack for execution.
- The
console.log('Inside setTimeout (macrotask)')is printed. - Now, the callback for
setTimeoutis popped from the call stack after execution.
- The
Event Loop: Handling setTimeout (Macrotask Queue)
- Event Loop: Once all synchronous code finishes (i.e., when the call stack is empty), the event loop checks if there are any tasks in the macrotask queue (also known as the task queue).
- Macrotask Queue: The callback function from
setTimeout(after 1000 ms) is now ready to be executed, so it is moved to the macrotask queue. - The event loop picks up the task from the macrotask queue and moves it to the call stack for execution:
console.log('Inside setTimeout (macrotask)')is printed.- After executing this, the callback for
setTimeoutis popped from the call stack.
Event Loop: Handling Microtasks (Microtask Queue)
- Microtask Queue: The microtask queue holds tasks that need to be executed after the current script finishes but before the event loop picks up any new tasks from the macrotask queue.
- After the macrotask (i.e., the
setTimeoutcallback) is executed, the event loop checks and executes tasks in the microtask queue. The first task in the queue is:console.log('Inside first Promise then (microtask)')is printed.
- The next microtask is the promise inside the
setTimeoutcallback:console.log('Inside Promise then (microtask)')is printed.
- The last microtask is the promise from the global context:
console.log('Inside last Promise then (microtask)')is printed.