📕
TIC
  • Tehnologii ale InformaÅ£iei ÅŸi ComunicaÅ£iilor (TIC)
  • Basic web principles
    • How web pages work
    • The pillars of a web page
    • Extra: Getting Started with GitHub
  • Basic HTML
    • Description and Basic Syntax
    • Extra resources
  • Basic CSS
    • Description and Basic Syntax
    • Advanced Positioning
    • Extra Resources
  • Basic Javascript
    • Description and basic syntax
    • The Document Object Model
    • Extra Resources
    • Basic assignment
    • The Color Game
  • Advanced Javascript
    • Runtime Engine, Callstack, Scope
    • ES6
  • Advanced Javascript 2
  • Programming paradigms
  • OOP Javascript
  • Functional Programming
  • OOP vs. Functional Programming
  • Asynchronous Javascript
  • Backend Javascript
    • NodeJS
    • ExpressJS
    • REST APIs
    • Authentication and Authorization
  • Firebase
    • NoSQL Databases
    • Database as a Service
    • Google Cloud Firestore
    • CRUD operations
    • Securing your database
  • Basic VueJS
  • Agenda: VueJS and Frontend Frameworks
  • Single Page Applications
  • VueJS basic syntax
  • Vue Components
  • Advanced VueJS
  • Advanced apps with Vue CLI
  • Vue Router
  • SPA State Management - Vuex
  • Composition API
  • Evaluation
    • Final Individual assignment
Powered by GitBook
On this page
  • JIT compilers
  • JavaScript runtimes
  • Scope

Was this helpful?

  1. Advanced Javascript

Runtime Engine, Callstack, Scope

PreviousThe Color GameNextES6

Last updated 7 months ago

Was this helpful?

JIT compilers

In modern browsers and in other environments such as NodeJS, JavaScript is a Just In Time Compiled language, which is a mix between Ahead Of Time Compilation and Interpretation.

After parsing and analyzing the source code, this approach achieves both interpretation followed by execution the explicit source code, and generating an optimized low-level language (Bytecode) which can be later executed if a similar context appears in the original source code thread.

A process called Hoisting is also performed before execution. All declarations, for both variables and functions, are processed first during the compilation phase before any of the code is executed. The declaration code is moved, or hoisted, from where they appear in the flow of the code to the top of the scope and pushed to the heap. Only declarations are hoisted, while assignments or other executable logic is left in place. Functions declarations are hoisted first, then variable declarations.

Therefore a function can be called even if it is explicitly described further in the code and variables declared with var do not throw errors if referenced before being assigned any value.

loggToConsole() //it will print Hello
x //it will print undefined

function loggToConsole() {
  console.log('Hello')
}

var x = 5

JavaScript runtimes

Popular JS runtimes (engine and additional methods) are V8 (behind Chrome, recently Microsoft Edge and NodeJS), JavaScriptCore (Safari) and SpiderMonkey (Mozilla Firefox).

The JS engine is single-threaded indeed but inside a runtime, we can achieve asynchronous computing. The anatomy of the runtime is described below.

The Call Stack operates as a Last-In-First-Out (LIFO) structure. When a function is called, it’s pushed onto the stack; once it completes, it's popped off. This process repeats until all functions are executed.

Once a task requires external APIs or methods (such as DOM manipulation, setting a timeout, or making an external API request), it triggers an asynchronous operation. When the operation completes, its callback is pushed to the Callback Queue. These callbacks will be executed in the order they were added, but only after the Call Stack is completely cleared of all synchronous tasks.

//Execution context
debugger //it allows step by step execution and evaluation
console.log('1')
setTimeout(function() {
	console.log('2')
}, 3000)
console.log('3')
console.log('4')
console.log('5')
setTimeout(function() {
	console.log('6')
}, 0)
console.log('7')
console.log('8')
console.log('9')

The microtask queue is a special queue that is processed by the JavaScript engine before the callback queue. This means that microtasks, such as promises or mutation observers, are always executed before any tasks from the callback queue, even if the callback was scheduled first.

console.log("Start");

// Callback queue (setTimeout)
setTimeout(() => {
  console.log("Timeout callback");
}, 0);

// Microtask queue (Promise)
Promise.resolve().then(() => {
  console.log("Promise microtask");
});

console.log("End");

// OUTPUT:

// Start
// End
// Promise microtask
// Timeout callback

Even though the setTimeout callback is initiated before the Promise, the promise is placed in the microtask queue and runs before the timeout, which is in the callback queue.

When you make a fetch request, the actual network operation is asynchronous, and the callback that handles the response (e.g., when the request finishes) is sent to the callback queue.

However, the response from fetch typically returns a promise. Once that promise is resolved (e.g., after processing the response), the .then() or .catch() handlers are sent to the microtask queue.

Thus, the initial fetch goes to the callback queue, and any promises yielded from it go to the microtask queue.

// here is an example of how HTTP fetches are added to the microtask queue

fetch('https://example.com/api/users')
  .then(response => response.json())
  .then(data => {
    // This callback will be executed after the HTTP fetch is complete
  });

HTTP response promises are added to the microtask queue with the following aims:

  • It ensures that HTTP fetches are executed as soon as possible.

  • It prevents HTTP fetches from blocking the main thread.

  • It allows developers to write more efficient and responsive asynchronous code.

Scope

The Scope is the context in which values and expressions are "visible" or can be referenced. If a variable or other expression is not "in the current scope," then it is unavailable for use. Scopes can also be layered in a hierarchy, so that child scopes have access to parent scopes, but not vice versa.

var x = "declared outside function";

exampleFunction();

function exampleFunction() {
    console.log("Inside function");
    console.log(x);
}
//Scoping
var favouriteFood = "grapes";

var foodThoughts = function () {
     console.log("Original favourite food: " + favouriteFood);

     var favouriteFood = "sushi";

     console.log("New favourite food: " + favouriteFood);
};

foodThoughts()

Function parameters have local scope within that function's body.

Declaring variables with let turns the scope to block scope, meaning it basically creates a clone (a mask) if that variable has already been declared in the parent scope. More details on the ES6 features.

V8 JS engine compiling example
Example of the scope chain