JavaScript: Execution Context, Call Stack, and Event Queue

Alexandra Williams
4 min readJun 14, 2017

--

Although Javascript (JS) is an interpreted language, meaning that is it run line by line rather than being run after all the lines of code are compiled, the language still has its own form of a compiler, run in what’s known as the Javascript engine. Every web browser has its own form of a JavaScript engine, though they all have the same purpose; JavaScript engines simply convert JavaScript source code into a language the complier can understand, then executes it.

But how does the engine know how to execute the code? Every line of code in JS has an execution context. Execution context controls what variables, functions, objects, etc. that a particular piece of code has access to. Since functions are what allow JS code to execute, the way functions encapsulate other variables, functions, and objects, (and the way functions are encapsulated by variables, functions, and objects), determine how the execution context and lexical scope will be structured. When a function executes, an execution context is created and virtually placed on top of the global execution context, the stacking of execution context is called the call stack. Once a function has finished executing, it gets popped off of the call stack.

There are two phases to creating the call stack…

The Creation Phase

The first step of the creation phase is creating the the global execution context. This creates the global environment, which in most cases for Javascript, would be a window in a web browser. It happens through three actions:

Action 1: Creates a Reference to “this”

Every environment (or execution context) has a reference to ‘this’, which refers to the container of of the execution context. In the case of the global execution context, ‘this’ refers to the window, and the reference is created during the creation phase.

Action 2: Creates Variable Environment

The compiler looks for variables and function declarations in the global environment and adds them to memory. This is what is called hoisting. Before the code is executed, the engine sees that there already variables and functions defined, and looks for their definitions through the scope chain.

It is important to note that, although variables and functions declarations in the global environment are stored in memory immediately, their definitions are not. They are stored as undefined. The engine reads their definitions during execution.

Action 3: Creates Connection to Outer Environment

The last step is creating a connection to its outer environment via the scope chain. This tells the execution context what environment contains it. For the global environment, the outer environment is null. However, all environments within the global, have the global environment as its outer environment. For example, a function ‘a’, contained in function ‘b’, has an outer environment of function b.

Execution Phase

During the execution phase, the engine now simply executes the code. It reads the code line by line, looking for functions to invoke. Each function has its own execution context and lexical environment, which gets stacked on top of the global execution context. During the functions execution, the engine looks in the functions lexical environment, looking for reference to execute the function. If it doesn’t find it in its own, it will continue to move up the scope chain until it reaches the global environment. If no references are found in the global environment it will return an error. However, if a reference is found and the function is executed correctly, the execution context of this particular function will be popped of the stack and the engine will move onto the next function, where their execution context will be added to the stack and executed, and so on.

var x = 2;function a(){
console.log("Executing function b in function a");
b();
}
function b(){
console.log("I'm function b");
}
Call Stack for the code above

Event Queue

In Web browsers, functions can also be executed through events. For instance, when a user clicks a button, scrolls down the page, moves the mouse, etc., a function can be invoked by adding event listeners to your code, which automatically execute when it ‘hears’ the event (we won’t get into detail here). A user may select many events at a rate faster that the JS engine can execute them. When this happens, an event queue is created alongside the call stack. If an event is selected, but the engine is in the midst of executing code, the event will wait in queue until the engine is done. Once done, the engine looks in the queue to see if an event is present. If so, the event is popped of the list and a new execution context is created and executed. This process continues until the event queue is empty.

Summary

When running Javascript code, the Javascript engine that executes the code first creates a call stack, which has a global execution context, and one of more function execution context. Once a function is executed, it’s execution context is popped off of the stack. When using Javascript events to execute code in a browser, anytime a event is selected, an event queue is created that gives the code a list of functions to complete.

--

--