JavaScript runtime mechanism

Single thread (single main thread)

As you all know, one of the biggest features of the JavaScript language is single threading. What is single threading?

That is, only one thing can be done at a time. The main reason for this design is to prevent conflicts between users, such as two threads working on a DOM at the same time.

In H5, there is a new thread, Web Workers, that runs in the background, but this thread is controlled by the main thread and cannot manipulate the DOM.

Event Loop

Image from Philip Roberts’ talk “Help, I’m Stuck in an Event-Loop”

Objects are stored in the heap, and the variables in the stack actually hold a pointer to the objects in the heap.

The code in the execution stack starts executing, Methods in the stack may call webAPI(DOM manipulation, Ajax, timer) to add their callbacks to the callback queue(task queue). For example, if ajax is executed in the stack, and ajax callbacks are added to the callback queue when ajax runs, Callbacks must be added to the callback Queue until the timer expires. Callbacks are asynchronous

console.log(1);
let fn = () => { console.log(2) }
setLog (1) console.log(3) Wait for the timer to expire add fn to the event queue // The main thread will read as soon as the code in the stack is finished"Task queue"Execute the callback function corresponding to those events in turn. //Copy the code

Note that setTimeout() simply inserts the event into the “task queue” and must wait until the current code (stack) completes before the main thread executes its specified callback function. If the current code takes a long time, it may take a long time, so there is no guarantee that the callback will be executed at setTimeout().

Callback Queues follow first-in, first-out logic, and callbacks that are queued first are executed first

Macro and micro tasks

Look at a piece of code

Promise.resolve().then() => {console.log(1); }) console.log(2);setTimeout(() => { console.log(3); }) // The result is 2, 1, 3Copy the code

Is it a surprise? Then is the same as setTimeout. So let’s switch the order

console.log(2);
setTimeout(() => { console.log(3); }) Promise.resolve().then(() => { console.log(1); }) // The result is still 2, 1, 3Copy the code

Promise. Then is the micro task, setTimeout is the macro task, the micro task is executed immediately after the completion of the code in the execution stack, the macro task is executed before the macro task, and the macro task is executed after all the micro tasks are executed

Macro task

setTimeout setInterval (setImmediate)

Micro tasks

Promise.then, the browser puts its implementation into a microtask, MutationObserve incompatible, MessageChannel microtask

Let’s look at another piece of code

console.log(1);
setTimeout(function(){
    console.log(2);
    Promise.resolve(1).then(function(){
        console.log('promise')})})setTimeout(function(){
    console.log(3);
})
Copy the code

The order of input results in the browser is 1, 2 prmise 3 go first and execute stack console.log(1); First go to the first setTimeout, put the microtask into the queue, execute the microtask, execute the macro task after the execution of the microtask (browser process)

But that’s not the case in a node where the result is 1, 2, 3. The Promise node is a callback queue that runs out of callbacks in the current task queue before it runs out of microtasks

You heard that right, microtasks have their own execution queue

Web Workers

Web workers provide an easy way for Web content to run scripts in background threads. Threads can execute tasks without interfering with the user interface, meaning that the page does not fake death while executing scripts in Web workers.

A quick word about dedicated worker usage. A dedicated worker can only be used by the script that generated it

Main.js (main thread file)if(window.worker) {// Handle error compatibilitylet myWorker = new Worker('worker.js'); // Specify a script URI // get two input fieldslet first = document.getElementById('first')
    let first = document.getElementById('second'// The workers method takes effect via postMessage() and onMessage event handler first.onchange =function() {
        myWorker.postMessage([first.value,second.value]);
        console.log('Message posted to worker');
    }

    second.onchange = function() {
        myWorker.postMessage([first.value,second.value]);
        console.log('Message posted to worker');
    }
    
    myWorker.onmessage = function(e) {// Get the result of worker.js result.textContent = e.ata; console.log('Message received from worker'); }}Copy the code
worker.js

onmessage = function(e) {// The parameters passed in are in e.ata console.log('Message received from main script');
  var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
  console.log('Posting message back to main script'); postMessage(workerResult); // return data}Copy the code

For more information, please click

node

The node of the Event Loop

When Node.js starts, it initializes the event loop, which may invoke an asynchronous API call, timer, or call process.nexttick (), and then start processing the event loop.

The following figure shows a simplified overview of the sequence of operations for the event loop.

Note: Each box will be called the “phase” of the event cycle.

Each phase has a FIFO(first in, first out) queue that performs callbacks.

Summary of stage

  • Timer: This phase performs the callbacks defined by setTimeout() and setInterval().
  • I/O Callbacks: This phase performs almost all callbacks except the off callback, timer, and setImmediate().
  • Idle, prepare: Used only internally.
  • Polling: Retrieves new I/O events. Check whether any timer expires.
  • Check: setImmediate() calls the callback here.
  • To close the callback: socket.on(‘close’,…) And so on.

Detailed stage

The timer

Provided callbacks can be executed after the time specified by the timer, but not immediately; they are simply queued into the Timers stage. Note: Technically, the polling phase controls when the timer is executed

I/O callback

This phase performs callbacks for certain system operations (reading and writing files, and so on).

polling

This stage has two main functions: 1. Execute and timer up to time; 2. Process events in polling queue

When entering this phase and there is no timer:

  • If the polling queue is not empty, the event loop will traverse its callback queue, executing them synchronously until the queue is exhausted or the system-related hard limit is reached.

  • If the polling queue is empty, one of two things happens:

    • If setImmediate() is already called, then the polling phase ends and the checking phase proceeds
    • Without the setImmediate() call, the event loop will wait for callbacks to be added to the queue and then execute them immediately

Once the polling queue is empty, the event loop checks for timers whose time has reached. If one or more timers are ready, the event loop falls back to the timer phase to execute the callbacks of those timers.

check

The stage of performing setImmediate()

Microtasks in Node

The microtasks in Node include then and nextTick

They are not part of the event loop and the microtasks have their own queue, which will be processed after the current operation completes regardless of the stage of the event loop. That is, to clear the microtask queue before the current phase ends and the next phase begins.

The last

Please give me more advice if you understand something wrong.