11001

JavaScript Event Loop (Browser and Node.js)

What is event loop? JavaScript is a single-thread language (one operation/ task at the time) and event loop is mechanism/ algorithm (endless loop) which helps handle asynchronous operations. Because of event loop thread will not block when multipe process should be executed (To coordinate events, user interaction, scripts, rendering, networking, and so forth) Consumes CPU and memory. Not a part of js engine, PART OF ENVIRONMENT (browser, node.js) When we invoke a function, a new execution context gets created which gets pushed onto the Call Stack. At any moment, only one frame is executing on the call stack. JavaScript is only able to handle one task at a time; if one task is taking too long to pop off, no other tasks can get handled.

Browser architecture, template, storage types, engines browser/ rendering/ js
JS engine controls call stack and heap, manage memory/ garbage collection, compilation to binary

Algorithms which participate in process:
Stack
Queue


Event loop mechanism a different depend on environment - Browser and Node.js


When fn started executed - it will be FULLY completed (it can't be interupted to run another part of code from different place) before next operations execute

EXPLAIN STEP BY STEP

Browser:

Event loop - check if call stack is empty and pushes tasks from microtask queue and tasks queue to call stack to execute
The Event Loop algorithm consists of four key steps:

Call Stack - when fn is execute it goes to call stack, when it executed it goes out. All sync operations will be executed immidiately (fns, variables declaration) IT CONTROLLED BY JS ENGINE, stackoverflow not the same as memory overflow?
Macrotask queue (Tasks queue/ Callback queue) - other APIs (setTimeout, setInterval, DOM events and browser features), I/O callbacks (THIS IS CALLBACKS QUEUE) you can create macrotask using setTimeout

Callback queue micro and task queues


The Task Queue is used by callback-based Web APIs to enqueue the callbacks once the asynchronous task has completed.

Microtask queue - high priority queue. Microtasks come solely from our code (.then/catch/finally!!!!!! not constructor of Promises, but promise handlers, MutationObserver callbacks, queueMicrotask()) callbacks, all Mutation API
Microtasks can also schedule other microtasks! This could create a scenario where we create en infinite microtask loop, delaying the Task Queue indefinitely and freezing the rest of the program. So be careful!  async fns: async () => { ....
Calling fetch creates a Promise Object in memory, which is "pending" by default. After initiating the network request, the fetch function call is popped off the Call Stack.


Web APIs - browser features, DOM events, network requests, handles all it's features independently, outside call stack, non-blocking operations main thread of event loop, event listeners, MOVES CALLBACKS TO MACROTASKS, provide a set of interfaces to interact with features that the browser (provide examples)
https://developer.mozilla.org/en-US/docs/Web/API
bridge between the JavaScript runtime and the browser features
The function is then popped off the Call Stack; it's now the browser's responsibility.
Some of these APIs allow us to initiate async tasks in the background.

Flow (ALL GOES QUEUE PRINCIPLE, nesting principle, endless loop):
1. Evaluate Script and schedule operations, sync operations (ALL, Synchronously execute the script as though it were a function body. Run until the Call Stack is empty. Raskidat vse chasti po task, microtask queue - SCHEDULES ALL OPERATIONS, CALL everything) STACK IS EMPTY GO TO NEXT. After initiating the async task (without waiting for the result), the execution context can quickly get popped off the Call Stack; it's non-blocking!
This is really just to "register" its callbacks to the Web API, which then offloads the operation to the browser.
The function call that initiates the async task is added to the Call Stack, but that is just to hand it off to the browser.
2. Micro tasks (ALL tasks)
4. Render UI changes if any (Rendering never happens while the engine executes a task. It doesn’t matter if the task takes a long time. Changes to the DOM are painted only after the task is complete.)
3. Tasks/ Macro tasks (Only one, one at a time per event loop tick,)
4. Repeat

alert() - browser API, pauses event loop in browser (freezes all js execution)


Promise handler callbacks (then(callback), catch(callback), and finally(callback))



Execution of async function bodies following await



MutationObserver callbacks



queueMicrotask callbacks



 Therefore, the code that handles the completion of that asynchronous action is defined as a callback.

Rendering never happens while the engine executes a task. It doesn’t matter if the task takes a long time. Changes to the DOM are painted only after the task is complete.
JavaScript is single-threaded and runs on the same thread as rendering.
The browser queues these changes but cannot render because JavaScript is blocking the thread
DOM changes accumulate in memory (the DOM tree is updated, but not the pixels on screen)


  1. Key points:
  • new Promise((resolve, reject) => { ... }) - the executor function runs immediately and synchronously when the Promise is constructed

Only the .then(), .catch(), .finally() handlers are asynchronous (microtasks)


Call order = top to bottom in code
Return order - last called, first returned


The Task Queue (macrotask queue) contains callbacks that were triggered by Web APIs or other asynchronous events.

event loop managed by js engine, tasks by browser?

For long heavy calculations that shouldn’t block the event loop, we can use Web Workers. In parallel, another thread, Web Workers can exchange messages with the main process, don't have access to the DOM. Web Workers do not have access to DOM, so they are useful, mainly, for calculations, to use multiple CPU cores simultaneously.

When the Call Stack is empty, the Event Loop first moves tasks from the Microtask Queue until this queue is completely empty. Then, it moves on to the Task Queue, where it moves the first available task to the Call Stack. After handling the first available task, it "starts over" by again checking the Microtask Queue.

Rendering queue, requestAnimationFrame, requestIdleCallback??
event loop exists only for tab?

if you have huge task:
1. split it in small chunks in setTimeout
2. use workers with postMessage to main thread
3. or use server

Examples:
const pause = (millis) =>
new Promise(resolve => setTimeout(resolve, millis));

const start = Date.now();
console.log('Start');

pause(1000).then(() => {
const end = Date.now();
const secs = (end - start) / 1000;
console.log('End:', secs);
});


------
EXPLAIN ONLY interpretation
A JavaScript engine is simply a computer program that executes JavaScript code. It's responsible for translating human-readable JavaScript code into machine-readable instructions that the computer's hardware can execute.

When you write JavaScript code and run it in a browser, the code doesn't directly interact with your computer's hardware. Instead, it interacts with the JavaScript engine, which acts as an intermediary between your code and the underlying machine.

Any JS engine always contains a call stack and a heap.

The call stack is where our code gets executed with the help of the execution context.

In compilation, the entire code is converted into machine code at once and written in a binary file that can be executed by a computer.
In interpretation, the interpreter runs through the source code and executes it line by line. The code still needs to get converted into machine code, but this time it is happening line by line while executing the program.

JS used to be a purely interpreted language. But the modern JS engine now use a mix of compilation and interpretation which is known as "just-in-time" compilation In JIT compilation, the entire code is converted into machine code at once and then executed immediately.

In JIT compilation, the entire code is converted into machine code at once and then executed immediately.`


You may wonder what's the difference between the compilation and JIT. Well, there's one major difference: after compilation, the machine code is stored in a portable file. It can be executed at any time – there's no need to rush immediately after the compilation process. But in the case of JIT, the machine code needs to execute as soon as the compilation ends. I'm not going to go in-depth into these concepts, but now you hopefully understand the basics of the compilation, interpretation and JIT.

During this parsing process, the code is parsed into a data structure called the AST (Abstract Syntax Tree). This works by first splitting up each line of code into pieces that are meaningful to the language (like the const or function keywords), and then saving all these pieces into the tree in a structured way. This step also checks if there are any syntax errors. The resulting tree will later be used to generate the machine code.

The next step is compilation. Here the engine takes the AST and compiles it to machine code.

But this is not the end. The modern JS engine generates inefficient machine code just to execute the program as fast as possible. Then the engine takes the already pre-compiled code to optimise and recompile the code during the already running program execution. All this optimisation happens in the background

Parsing -> AST -> Byte code -> Machine code -> Code runs on CPU

The browser’s high level structure

The key elements of the browser comprise:

1. User Interface: This encompasses the address bar, back/forward buttons, and bookmarking menu. It encompasses every aspect of the browser display except the main window where the requested page is visible.

2. Browser Engine: This is the interface for querying and manipulating the rendering engine.

3. Rendering Engine: Its primary role is to display the requested content. For instance, when the requested content is in HTML, it handles tasks like parsing the HTML and CSS and presenting the parsed content on the screen.

4. Networking: Utilized for network calls, like HTTP requests, with a platform-independent interface that includes specific implementations for each platform.

5. UI Backend: Employed for drawing fundamental widgets such as combo boxes and windows. It provides a generic interface not tied to any particular platform, using the underlying operating system’s user interface methods.

6. JavaScript Interpreter: This interprets and executes JavaScript code.

7. Data Storage: As a persistence layer, this component ensures that the browser can store various data types, such as cookies, on the hard disk. The HTML5 specification introduces a ‘web database,’ a lightweight database integrated into the browser.

The renderer process takes care of
- HTML parsing
- CSS parsing
- Image decoding
- JavaScript interpreter
- Regular expressions
- Layout
- Document Object Model
- Rendering
- SVG
- XML parsing
- XSLT

The browser process takes care of
- Cookie database
- History database
- Password database
- Window management
- Location bar
- Safe Browsing blacklist
- Network stack
- SSL/TLS
- Disk cache
- Download manager
- Clipboard

Machine code is a low-level programming language that directly represents the instructions executed by a computer’s central processing unit (CPU). It consists of binary code, usually in the form of a sequence of 0s and 1s, which corresponds to specific CPU instructions. Machine code is executed directly by the computer’s hardware. Each instruction in machine code corresponds to a specific operation that the CPU can perform. Machine code is often specific to a particular computer architecture or CPU.


In summary, machine code is more closely tied to the hardware and is platform-specific, while bytecode provides a level of abstraction that allows for greater portability across different platforms. The trade-off is often between performance (machine code) and portability (bytecode)💡.

JavaScript engines are interpreters that parse and execute JavaScript code. Modern JavaScript engines use just-in-time (JIT) compilation to convert JavaScript code into machine code that can be executed by a computer's processor. A JavaScript engine is typically developed and used in web browsers to run client-side code but can also be used in server-side environments like Node.js.

In a browser, the JavaScript engine operates together with the rendering engine via the Document Object Model and Web IDL bindings. Some JavaScript engines also execute WebAssembly code in the same sandbox as regular JavaScript code.

Do not confuse JavaScript engines with rendering engines, which are also crucial parts of browsers.

Rendering engines (also known as layout engines or browser engines) are part of a web browser that transforms HTMLCSS, and other resources of a web page into a visual representation on a screen.

Common rendering engines include:

WebKit


Whereas HTML defines a webpage's structure and content and CSS sets the formatting and appearance, JavaScript adds interactivity to a webpage and creates rich web applications.

However, the umbrella term "JavaScript" as understood in a web browser context contains several very different elements. One of them is the core language (ECMAScript), another is the collection of the Web APIs, including the DOM (Document Object Model).

At a high level, you could say that a JavaScript engine is a program that takes your JavaScript (JS) code and turns it into something that your computer can actually run.
Each browser has their own JavaScript engine, designed to deliver the best performance to their users:

Modern engines use multiple techniques to make sure your JavaScript runs fast and is memory efficient:
JIT

Inline Caching

In the process of accessing an object property, Engine checks Cache locations at first, if it is found that will reduce the overall execution time used to lookup for a place where property actually is.

Example: It basically means if you access user.name multiple times, engine will remember object where user data is stored so that next time it does not have to find that object again and it can be directly accessed.

JavaScript engines run alongside the browser's rendering engine. When you visit a website:

  • The browser will first of all fetch HTML, CSS and JavaScript files.
  • JavaScript engine in the browser will parse and execute those scripts.
  • The rendering engine combines the processed HTML, CSS, and JavaScript to display the webpage