An overview of JavaScript Engine, Runtime, and Call Stack - rohit120582sharma/Documentation GitHub Wiki

JavaScript Runtime Environment

A runtime environment is the execution environment provided to an application by the operating system. In a runtime environment, the application can send instructions or commands to the processor and access other system resources such as RAM, DISK etc. JS engine, Event queues, Event loop and Web/Dom APIs forms the Runtime Environment.

There are APIs in the browser that have been used by almost any JavaScript developer out there (e.g. setTimeout). Those APIs, however, are not provided by the Engine.

We have those things called Web APIs which are provided by browsers, like the DOM, AJAX, setTimeout and much more.

Runtime



JavaScript Engine

A JavaScript engine is a program or an interpreter which executes JavaScript code. A JavaScript engine can be implemented as a standard interpreter, or just-in-time compiler that compiles JavaScript to bytecode in some form.

It does not handle any kind of web/DOM events such as click events, page load events, ajax calls etc.

The Engine consists of two main components:

  • Memory Heap — this is where the memory allocation happens
  • Call Stack — this is where your stack frames are as your code executes

Heap memory is used to allocate memory for the variables, functions etc whereas Call Stack is a data structure used to execute our JS code.

There are actually several different implementations of JavaScript engines but by far the most popular version is Google Chrome’s V8 engine (which is not limited to the browser but also exists in the server via NodeJS).

V8 was first designed to increase the performance of JavaScript execution inside web browsers. In order to obtain speed, V8 translates JavaScript code into more efficient machine code instead of using an interpreter. It compiles JavaScript code into machine code at execution by implementing a JIT (Just-In-Time) compiler like a lot of modern JavaScript engines do such as SpiderMonkey or Rhino (Mozilla). The main difference here is that V8 doesn’t produce bytecode or any intermediate code.

JavaScript Engine inside

The Call Stack

JavaScript engine's job is to go through all the lines of JavaScript in an application and process them one at a time which means that JavaScript is single-threaded. So if you’re running a line of JavaScript that happens to take a long time to return, then all the code after that will be blocked.

JavaScript engine uses a call stack to know and process a single line of JavaScript at a time. You can think of a call stack like entering an elevator — the first person who enters the elevator is the last person to exit the elevator, whereas the last person to enter is the first to exit.

/* Within main.js */

var firstFunction = function(){
	console.log("I'm first!");
};
var secondFunction = function(){
	firstFunction();
	console.log("I'm second!");
};
secondFunction();

/* Results:
* => I'm first!
* => I'm second!
*/


Concurrency & the Event Loop

JavaScript provides a mechanism to avoid blocking code via asynchronous callback functions.

An asynchronous callback function is just like any other function you’re used to writing in JavaScript, with the added caveat that it doesn’t get executed till later.

JavaScript’s setTimeout function is an example of asynchronous callback functions.

/* Within main.js */

var firstFunction = function(){
	console.log("I'm first!");
};
var secondFunction = function(){
	setTimeout(firstFunction, 5000);
	console.log("I'm second!");
};
secondFunction();

/* Results:
* => I'm second!
* (And 5 seconds later)
* => I'm first!
*/

Event Loop

Event Table

Right after the setTimeout function is executed, something special happens here - the browser places setTimeout’s callback function (in this case, firstFunction) into an Event Table. Think of the event table as a registration booth: the call stack tells the event table to register a particular function to be executed only when a specific event happens.

Event Queue

When the event does happen, the event table will simply move the function over to the Event Queue. The beauty of this event queue is that it's simply a staging area for functions waiting to be invoked and moved over to the call stack.

Event Loop

It is a process that constantly checks whether the call stack is empty, and whenever it’s empty, it checks if the event queue has any functions waiting to be invoked. If it does, then the first function in the queue gets invoked and moved over into the call stack. If the event queue is empty, then this monitoring process just keeps on running indefinitely.

Different execution strategy of tasks queued in task and micro-task queue

As mentioned before, event loop will pick up a new task from a task queue only when call stack is empty and there is nothing to execute in a micro-task queue.

When JS engine, traverses through the code within a function and encounters web API events such as click, keydown etc, it delegates the task to the runtime environment and now runtime decides where should it queue it’s callback handler (either in task queue or micro-task queue?).

According to the standard specification, callback handlers of following events are queued in task queue

  • DOM/Web events (onclick, onkeydown, XMLHttpRequest etc)
  • Timer events (setTimeout(…), setInterval(…))

Similarly, callback handlers following objects are queued in a micro-task queue

  • Promises (resolve(), reject())
  • Browser observers (Mutation observer, Intersection Observer, Performance Observer, Resize Observer)

References

⚠️ **GitHub.com Fallback** ⚠️