How does code actually run in Javascript? (Part 1 — Synchronicity of JS Engine)

Yasin Ulusoy
Beyn Technology
Published in
7 min readNov 29, 2022

JavaScript is a programming language that, along with HTML and CSS, is one of the core technologies of the World Wide Web. Although it is widely used for web development and web applications, there are various usage of JavaScript from developing web servers to controlling IoT devices. The one aspect making it popular is the variety of usage fields. There are many different sources about the usage fields and technical background but in this article, the main purpose is explaining how our JavaScript code is run behind the scenes.

Fundamentally, JavaScript is a synchronic programming language, and the synchronicity is provided by three principles which are Thread of Execution, Memory, and Call Stack.

Thread of Execution

“Thread” in software is the smallest sequence of execution of algorithmic instructions managed by a scheduler included by operating system. Originally, JavaScript was designed to run scripts on websites. Therefore, JavaScript Thread is single-threaded by default and it is the reason for synchronicity. To make it clear, it can be explained as; code blocks go through line-by-line and runs/executes each line during each step. This is known as thread of execution.

Memory

The second principle of JavaScript can be defined as memory. While JavaScript code runs line by line, it encounters some situations such as storing a variable or constant or even code itself known as function. To store that stuff, it uses the computer’s memory to come back to later and use. In order to understand these two principles, let’s check the code below.

To understand how the code piece above works, we can start from line 1. You can see that num constant is defined and assigned a value which is 3. In order to store that constant and its value, JavaScript uses computer’s memory.

As you see, in line 2, the function multiplyBy2 is declared. Similar with num constant, JavaScript takes that function declaration and bundles it up then stores it in the computer’s memory.

After storing the function multiplyBy2, in line 6 output const is declared but when we check the right side of the equal sign, things are getting complicated and magic starts. At that moment, a brief about an important concept in JavaScript which is called Execution Context will help us to understand what is happening in line 6.

Execution Context

Execution Context can be defined as the environment in which a function is run. As it is explained above, in order to execute any code in JavaScript, we need two principles. In the global scope, the combination of these two principles of JavaScript is called “Global Execution Context”. On the other hand, a function being run is like a mini program. So, we need those two things. The thread of execution, the ability to go through the code line-by-line and do it. And a little store of data, the memory, to store anything that shows up while we’re inside that function. To imagine it, we can assume that for each running function, JavaScript engine creates mini environment to execute commands in the function.

Let’s return to code above, in line 6 when output const is declared. Before Its value is assigned, multiplyBy2 function will be executed. At that moment, a brand-new execution context will be created.

As you see above, when an execution context is created, thread of execution weaves into function, and local memory is assigned for inside of the multiplyBy2(3). Because of the inputNumber argument of the function, local memory stores inputNumber value in the first step. Then, const result is declared and its value stores in the local memory. In the last line of inside of the function, we have “return” keyword and our function returns the value of result const.

Another perspective about the process is that, until our multiplyBy2 function is executed, the variables inside of the functions were not created. When our execution context is created and local memory is assigned for that function, our local variables which are inside of the function begin to be created. Another important part about execution context is that similar with it is created as soon as a function is performing, when a function returns anything, it is destructured, that means it’s going to literally turn into the value. Then, the thread of execution weaves out from function to global scope.

As you see in the image, there are two function declarations which are getFullName and getPersonAge sequentially, and in line 11, there is a const which is personDetail. While our thread of execution go through line by line, javascript stores function declarations. And when thread of execution reaches to line 11, the value of personDetail is the result of the getPersonAge(33, “John”, “Doe”). At that moment our execution context for that function will be created.

Call Stack

What is Call Stack?

According to Mozilla Developer Network, A call stack is a mechanism for an interpreter (like the JavaScript interpreter in a web browser) to keep track of its place in a script that calls multiple functions — what function is currently being run and what functions are called from within that function, etc. JavaScript needs to track operations accurately and precisely. Call Stack provides that mechanism. We’re not going to focus on the call stack deeply, but a key thing with a stack is when you add something to it, when you run a function, we’re going to add it to our call stack. So, in our example above, when we start running multiply by two, we add it to our call stack, multiply by two with the input of three.

How does it work?

If there is not any function to execute, our call stack is busy to work on global execution context and if a function is about to start to execute, while execution context for the function is created, the function is added to the call stack and our call stack will be busy with the execution of that function. To make it clearer, we can examine the scheme below. It is the moment at which multiplyBy2(3) function is executed.

As you see above, in the call stack, in addition to global execution context, multiplyBy2(3) function is added. And we’ve already known that when a function execution context is finished (when the function returns anything) execution context will be ended and it will return to a value. Parallely, function will be pop off from the call stack. So, we can clearly say that when multiplyBy2(3) is executed, it will be removed from our call stack and const output will take value 6.

What if multiplyBy2 function includes another function call?

Let’s say our code is like below. How the JavaScript mechanism will be proceed?

In line 12, after output const declaration, execution context for multiplyBy2(3) will be created. We already know that. But the execution context will be a little different than the previous version.

In the execution context of output=multiplyBy2(3), a brand-new execution context will be created for adjustedResult=minusOne( ) and at that moment our call stack will be busy with processing of minusOne( ). When the minusOne function returns adjustedResult, the inner execution context will be finished, and our call stack will continue to be busy with multiplyBy2(3). And after multiplyBy2(3) is executed and output const gets value 5, the outer execution context will be finished too. And the call stack will be busy only with global execution context.

To Sum Up

Although there are deeply technical aspects of the JavaScript engine, the three main principles of how JavaScript code is run are briefly explained above. Synchronicity is provided as a result of these three working in harmony. To summarize the principles in a nutshell;

Javascript codes are executed line by line

As a result of single thread, javascript executes each line of code in every step.

Javascript uses the computer’s memory

Javascript stores any variable or function in the computer’s memory to use them whenever needed.

First two principles provide execution context. It is not an issue whether global execution context or function execution context, thread of execution and memory provides execution of any context (any commands).

Javascript needs to track which operation is running

As a result of single thread, Javascript needs to track all operations accurately. Call Stack mechanism provides that requirement and gives an answer to the following question; “Which operation is executed now?”

On the other hand, as all of us know, JavaScript not only provides synchronous programming but it also has rich asynchronous features. These features are especially used for connection with external sources and interaction with browsers. Asynchronous features will be briefly explained in part 2.

Resources

--

--