How JS Works: Overview of the engine, runtime mechanisms, call stack

Original author: Alexander Zlatkov
  • Transfer
The popularity of JavaScript is growing, its capabilities are used at different levels of technology stacks used by developers and on many platforms. On JS they do frontend and backend, write hybrid and embedded applications, and much more.

Analysis of GitHub statistics shows that in terms of active repositories and push requests, JavaScript is in the first place, and in other categories it shows rather high positions. JavaScript Statistical Information with GitHub Another GitHub statistical information system can be found here , it confirms what was said above.






If many projects are tightly tied to JavaScript, it means that developers need to use everything that the language and its ecosystem gives them as efficiently as possible, striving, on the way to developing wonderful programs, to deeply understand the internal mechanisms of the language.

Oddly enough, there are many developers who regularly write in JavaScript, but do not know what is happening in its depths. The time has come to fix it: this material is devoted to a review of the JS engine using the example of V8, runtime mechanisms, and call stacks.

Overview


Almost everyone has heard, in the most general terms, about the V8 JS engine, and most developers know that JavaScript is a single-threaded language, or that it uses a callback function queue.

Here we will talk, at a fairly high level, about the execution of JS code. Knowing what actually happens when JavaScript is executed, you can write better programs that run without “hangs” and make reasonable use of the available APIs.

If you recently started writing in JavaScript, this material will help you understand why JS, in comparison with other languages, may seem rather strange.

If you are an experienced JS developer, we hope this material helps you better understand how what you use every day actually works.

JavaScript engine


V8 from Google is a well-known JS engine. It is used, for example, in the Chrome browser and in Node.js. Here's how it can be presented in a very simplified way:


Simplified representation of the V8 engine

In our diagram, the engine is presented consisting of two main components:

  • Heap (Memory Heap) - the place where the allocation of memory.
  • The Call Stack is where the so-called stack frames fall into the process of executing the code.

Runtime mechanisms


If we talk about the use of JavaScript in the browser, then there are APIs, for example, something like functions setTimeoutthat almost every JS developer uses. However, these APIs are not provided by the engine.

Where do they come from? It turns out that reality looks a little more complicated than it might seem at first glance.


The engine, the event loop, the queue of callback functions and APIs provided by the browser

So, in addition to the engine, we still have a lot of things. Say - the so-called Web API that the browser provides us with - tools for working with the DOM, tools for executing AJAX requests, something like a function setTimeout, and much more.

Call stack


JavaScript is a single-threaded programming language. This means that it has one call stack. Thus, at a certain point in time he can perform only one task.

The call stack is a data structure that, in simple terms, records information about the place in the program where we are. If we go into a function, we put a record about it at the top of the stack. When we return from the function, we pull the topmost element from the stack and find ourselves where we called this function from. This is all the stack can do.

Consider an example. Take a look at the following code:

function multiply(x, y) {
    return x * y;
}
function printSquare(x) {
    var s = multiply(x, x);
    console.log(s);
}
printSquare(5);

When the engine is just starting to execute this code, the call stack is empty. After this, the following occurs:

Call stack during program execution

Each entry in the call stack is called a stack frame .

The call stack analysis mechanism, based on the call stack information, and the stack trace that is issued when an exception occurs, are based on the stack analysis mechanism. The stack trace is the state of the stack at the time of the exception. Take a look at the following code:

function foo() {
    throw new Error('SessionStack will help you resolve crashes :)');
}
function bar() {
    foo();
}
function start() {
    bar();
}
start();

If you do this in Chrome (it is assumed that the code is in the file foo.js), we will see the following stack information:


Trace the stack after an error occurs

If the maximum stack size is reached, a so-called stack overflow will occur. This can happen quite simply, for example, with the thoughtless use of recursion. Take a look at this piece of code:

function foo() {
    foo();
}
foo();

When the engine starts executing this code, it all starts with a function call foo. This is a recursive function that does not contain a condition for terminating a recursion. She calls herself uncontrollably. As a result, at each execution step, information about the same function is added to the call stack over and over again. It looks something like this:

Stack overflow

At some point, however, the amount of data about the function calls will exceed the size of the call stack and the browser decides to intervene, giving an error:


Exceeding the maximum size of the call stack

The single-threaded code execution model makes life easier for the developer. He does not need to take into account complex schemes of interaction of software mechanisms, such as the possibility of mutual blocking of flows that arise in multi-threaded environments.

However, the execution of the code in single-threaded mode also has certain limitations. Given that JavaScript has one call stack, let’s talk about what happens when a program slows down.

Concurrent code execution and event loop


What happens when a call stack has a function that takes a lot of time to complete? For example, imagine that you need to perform some kind of difficult image conversion using JavaScript in a browser.

“What is the problem here?” You ask. The problem is that as long as there is a function in the call stack, the browser cannot perform other tasks - it is blocked. This means that the browser cannot display anything on the screen, it cannot execute other code. He just stops. Similar effects, for example, are not compatible with interactive interfaces.

However, this is not the only problem. If the browser begins to handle heavy tasks, it may stop responding to any influences for quite some time. Most browsers in this situation throw an error asking the user if he wants to complete the script and close the page.


The browser suggests completing the page.

Users will definitely not like these things.

So, how do you perform heavy computing without blocking the user interface and without suspending the browser? The solution to this problem is to use asynchronous callback functions. This is a topic for another discussion.

Summary


In general terms, we looked at the design of the JS engine, runtime mechanisms, and the call stack. Understanding the concepts presented here can improve the quality of the code.

Dear readers! This material is the first in a series of How JavaScript Works from the SessionStack blog . The second one has already been published - dedicated to the features of V8 and code optimization techniques. Do you think it's worth translating it?


Also popular now: