Home  >  Article  >  Web Front-end  >  Completely master the operating mechanism and principles of JavaScript

Completely master the operating mechanism and principles of JavaScript

WBOY
WBOYforward
2022-04-25 17:05:203102browse

This article brings you relevant knowledge about javascript. It mainly introduces related issues about the JavaScript operating mechanism and principles, including parsing engines and other contents. Let’s take a look at it together. I hope everyone has to help.

Completely master the operating mechanism and principles of JavaScript

[Related recommendations: javascript video tutorial, web front-end]

I have been writing js for more than two years , I have never understood its operating mechanism and principles very well. Today I specially recorded the theories of the masters and my own summary below:

What is JavaScript parsing engine

Simply put, a JavaScript parsing engine is a program that can "read" JavaScript code and accurately provide the results of the code execution.

For example, when you write a piece of code like var a = 1 1;, what the JavaScript engine does is to understand (parse) your code and change the value of a to 2.

Those who have studied the principles of compilation know that for static languages ​​​​(such as Java, C, C), the one that handles the above things is called a compiler (Compiler), and correspondingly for dynamic languages ​​​​such as JavaScript, it is called a compiler. Interpreter. The difference between the two can be summarized in one sentence: the compiler compiles the source code into another type of code (such as machine code, or bytecode), while the interpreter directly parses and outputs the code running results. For example, Firebug's console is a JavaScript interpreter.

However, it is now difficult to define whether the JavaScript engine is an interpreter or a compiler, because, for example, V8 (Chrome's JS engine) actually uses the code to improve the running performance of JS. Before running, JS will be compiled into native machine code (native machine code), and then the machine code will be executed (this will be much faster).

What is the relationship between JavaScript parsing engine and ECMAScript

The JavaScript engine is a program, and the JavaScript code we write is also a program. How to make the program understand the program? This requires defining rules. For example, the previously mentioned var a = 1 1;, it means:

The var on the left represents a declaration, which declares the variable a.
The one on the right represents the difference between 1 and 1 Do addition
The equal sign in the middle indicates that this is an assignment statement
The semicolon at the end indicates the end of the statement
These are the rules. With it, there is a standard of measurement, and the JavaScript engine will JavaScript code can be parsed according to this standard. Then the ECMAScript here defines these rules. Among them, the document ECMAScript 262 defines a complete set of standards for the JavaScript language. These include:

var, if, else, break, continue, etc. are JavaScript keywords
abstract, int, long, etc. are JavaScript reserved words
How to count it as a number and how to count it as a string Etc.
Defines operators ( , -, >, Defines JavaScript syntax
Defines standard processing algorithms for expressions, statements, etc., such as when encountering == How to deal with
⋯⋯
The standard JavaScript engine will be implemented according to this set of documents. Note that the standard is emphasized here, because there are also implementations that do not follow the standard, such as IE's JS engine. This is why JavaScript has compatibility issues. As for why IE's JS engine is not implemented in accordance with standards, it comes to the browser war. I won't go into details here. Google it yourself.

So, to put it simply, ECMAScript defines the standard of the language, and the JavaScript engine implements it according to it. This is the relationship between the two.

What is the relationship between the JavaScript parsing engine and the browser?

Simply put, the JavaScript engine is one of the components of the browser. Because the browser has to do a lot of other things, such as parsing pages, rendering pages, cookie management, history records, etc. Well, since it is a component, JavaScript engines are generally developed by browser developers themselves. For example: Chakra of IE9, TraceMonkey of Firefox, V8 of Chrome, etc.

It can also be seen that different browsers use different JavaScript engines. So, all we can say is which JavaScript engine to get a deeper understanding of.

Why JavaScript is single-threaded

One of the major features of the JavaScript language is that it is single-threaded, that is to say, it can only do one thing at the same time. So why can't JavaScript have multiple threads? This can improve efficiency.

The single thread of JavaScript is related to its purpose. As a browser scripting language, JavaScript's main purpose is to interact with users and manipulate the DOM. This determines that it can only be single-threaded, otherwise it will cause very complex synchronization problems. For example, suppose JavaScript has two threads at the same time. One thread adds content to a certain DOM node, and the other thread deletes the node. In this case, which thread should the browser use?

So, in order to avoid complexity, JavaScript has been single-threaded since its birth. This has become the core feature of this language and will not change in the future.

In order to take advantage of the computing power of multi-core CPUs, HTML5 proposes the Web Worker standard, which allows JavaScript scripts to create multiple threads, but the child threads are completely controlled by the main thread and must not operate the DOM. Therefore, this new standard does not change the single-threaded nature of JavaScript.

Distinguish between processes and threads

A process is the smallest unit of CPU resource allocation, and a process can contain multiple threads. Browsers are multi-process, and each opened browser window is a process.

Thread is the smallest unit of CPU scheduling. The memory space of the program is shared between threads in the same process.

The process can be regarded as a warehouse, and the thread is a truck that can be transported. Each warehouse has its own multiple trucks to serve the warehouse (carrying goods). Each warehouse can be pulled by multiple vehicles at the same time. Cargo, but each vehicle can only do one thing at a time, which is to transport this cargo.

Core point:

The process is the smallest unit of CPU resource allocation (the smallest unit that can own resources and run independently)

The thread is the CPU The smallest unit of scheduling (a thread is a program running unit based on a process, and there can be multiple threads in a process)

Different processes can also communicate, but at a higher cost.
Browsers are multi-process
After understanding the difference between processes and threads, let’s understand the browser to a certain extent: (Let’s look at the simplified understanding first)

Browsers are multi-process The process's

The reason why the browser can run is because the system allocates resources (cpu, memory) to its process

To understand simply, every time you open a Tab page, it is equivalent to creating a A separate browser process.

Take Chrome as an example. It has multiple tab pages, and then you can see multiple processes in Chrome's task manager (each Tab page has an independent process, and a main process. ), which can also be seen in the Windows Task Manager.

Note: The browser should also have its own optimization mechanism here. Sometimes after opening multiple tab pages, you can see in the Chrome Task Manager that some processes are merged (so each Tab label corresponds to A process is not necessarily absolute)

What processes does the browser contain?

After knowing that the browser is a multi-process, let’s take a look at what processes it contains: (To simplify understanding, Only the main processes are listed)

(1) Browser process: There is only one main process of the browser (responsible for coordination and control). Role:

  • Responsible for browser interface display and user interaction. Such as forward, backward, etc.
  • Responsible for the management of each page, creating and destroying other processes
  • Draw the Bitmap in the memory obtained by the Renderer process to the user interface
  • Network Management of resources, downloads, etc.

(2) Third-party plug-in process: Each type of plug-in corresponds to a process, which is only created when the plug-in is used
(3) GPU process: up to One, used for 3D drawing, etc.
(4) Browser rendering process (browser kernel, Renderer process, internally multi-threaded): By default, each Tab page has one process and does not affect each other. The main functions are: page rendering, script execution, event processing, etc.

Enhance memory: opening a web page in the browser is equivalent to starting a new process (the process has its own multi-threading)

Of course, browsers sometimes merge multiple processes (for example, after opening multiple blank tabs, you will find that multiple blank tabs are merged into one process)

Advantages of multiple browser processes

Compared with single-process browsers, multi-process browsers have the following advantages:

  • Avoid a single page crash affecting the entire browser
  • Avoid third-party plug-in crashes affecting the entire browser
  • Multiple processes take full advantage of multi-core advantages
  • Conveniently use the sandbox model to isolate plug-in and other processes to improve browser stability

Simple understanding:

If the browser is a single process, then the crash of a certain tab page will affect the entire browser, and the experience will be poor; similarly, if it is a single process, the crash of the plug-in will also affect the entire browser.

Of course, memory and other resource consumption will also be greater, which means that space is exchanged for time. No matter how large the memory is, it is not enough for Chrome. The memory leak problem has been improved a bit now. It is only an improvement, and it will also lead to increased power consumption.

Browser kernel (rendering process)

Here comes the key point. We can see that there are so many processes mentioned above. So, for ordinary front-end operations, what is ultimately needed is what? The answer is the rendering process.

It can be understood that the rendering of the page, the execution of JS, and the event loop are all performed within this process. Next, focus on analyzing this process

Please keep in mind that the browser's rendering process is multi-threaded (the JS engine is single-threaded)

Then let's take a look at it Which threads are included (list some main resident threads):

1. GUI rendering thread

  • is responsible for rendering the browser interface, parsing HTML and CSS, and building the DOM tree and RenderObject tree (simply understood as the style tree formed by CSS, one of the cores of Flutter), Layout and drawing etc.
  • When the interface needs to be repainted (Repaint) or a reflow is caused by a certain operation, this thread will be executed.
  • Note that the GUI rendering thread and the JS engine thread are mutually exclusive. , when the JS engine is executing, the GUI thread will be suspended (frozen), and the GUI updates will be saved in a queue and will be executed immediately when the JS engine is idle.

2. JS engine thread

  • is also called the JS kernel and is responsible for processing Javascript script programs. (For example, V8 engine)
  • The JS engine thread is responsible for parsing Javascript scripts and running code.
  • The JS engine has been waiting for the arrival of tasks in the task queue, and then processes them. There is only one JS thread running the JS program in a Tab page (renderer process) at any time.
  • Also note that , the GUI rendering thread and the JS engine thread are mutually exclusive, so if the JS execution time is too long, it will cause the rendering of the page to be incoherent, causing the page rendering and loading to be blocked.

3. The event trigger thread

  • belongs to the browser rather than the JS engine and is used to control the event loop (it is understandable that the JS engine itself is too busy and needs The browser opens another thread to assist)
  • When the JS engine wants to execute a code block such as setTimeOut (it can also come from other threads in the browser kernel, such as mouse clicks, Ajax asynchronous requests, etc.), the corresponding task will be added to in the event thread. And will be responsible for sorting
  • When the corresponding event meets the triggering conditions and is triggered, the thread will add the event to the end of the queue to be processed, waiting for processing by the JS engine
  • Note that due to JS Single-threaded relationship, so the events in the pending queue have to be queued to wait for processing by the JS engine (it will be executed when the JS engine is idle)
  • This can be simply understood as that it is responsible for managing a bunch of events and An "event queue", only the tasks in the event queue will be executed by the JS engine when it is idle, and what it has to do is to add it to the event queue when an event is triggered. For example, mouse click.

4. Timing trigger thread

  • The thread where the legendary setInterval and setTimeout are located
  • The browser timing counter is not counted by the JavaScript engine , (because the JavaScript engine is single-threaded, if it is in a blocked thread state, it will affect the accuracy of the timing)
  • Therefore, a separate thread is used to time and trigger the timing. After the timing is completed, it is added to the event queue (corresponding to "When the event meets the triggering conditions and is triggered" of the event triggering thread), wait for the JS engine to be idle before execution.
  • Note that W3C stipulates in the HTML standard that the time interval below 4ms in setTimeout is counted as 4ms.

5. Asynchronous http request thread

  • After XMLHttpRequest is connected, a new thread request is opened through the browser
  • When the status change is detected , if a callback function is set, the asynchronous thread will generate a state change event and put this callback into the event queue. Then executed by the JavaScript engine.

The communication process between the Browser process and the browser kernel (Renderer process)

Seeing this, first of all, you should have a certain understanding of the processes and threads in the browser, then Next, let’s talk about how the browser’s Browser process (control process) communicates with the kernel. After understanding this, we can connect this part of knowledge together to have a complete concept from beginning to end.

If you open the task manager and then open a browser, you can see: two processes appear in the task manager (one is the main control process, and the other is the rendering process that opens the Tab page) , and then under this premise, let’s take a look at the entire process: (It’s a lot simplified)

  • When the Browser process receives a user request, it first needs to obtain the page content (such as downloading resources through the network), and then transfer the The task is passed to the Render (kernel) process through the RendererHost interface
  • The Renderer interface of the Renderer process receives the message, briefly explains it, hands it to the rendering thread, and then starts rendering
  1. The rendering thread receives the request, loads the webpage and renders the webpage, which may require the Browser process to obtain resources and the GPU process to help render
  2. Of course there may be a JS thread operating the DOM (this may cause reflow and redrawing)
  3. Finally the Render process passes the result to the Browser process
  • The Browser process receives the result and draws the result

Here is a simple drawing Picture: (very simplified)
Completely master the operating mechanism and principles of JavaScript

The relationship between threads in the browser kernel

At this point, we have already learned about the operation of the browser An overall concept has been developed. Next, let’s briefly sort out some concepts

The GUI rendering thread and the JS engine thread are mutually exclusive

Since JavaScript can manipulate the DOM, if you modify the properties of these elements and render the interface at the same time (that is, the JS thread and the UI thread run at the same time), then the rendering thread before and after The obtained element data may be inconsistent.

Therefore, in order to prevent unexpected rendering results, the browser sets the GUI rendering thread and the JS engine to have a mutually exclusive relationship. When the JS engine is executed, the GUI thread will be suspended, and the GUI updates will be saved in Wait in a queue to be executed immediately when the JS engine thread is idle.

JS blocks page loading

From the above mutually exclusive relationship, it can be deduced that JS will block the page if the execution time is too long.

For example, assume that the JS engine is performing a huge amount of calculations. Even if the GUI is updated at this time, it will be saved in the queue and wait for the JS engine to be idle for execution. Then, due to the huge amount of calculations, the JS engine is likely to be idle for a long, long time, and naturally it will feel extremely huge.

Therefore, try to avoid JS execution taking too long, which will cause the rendering of the page to be inconsistent, leading to the feeling that the page rendering and loading are blocked.

To solve this problem, in addition to placing the calculation on the backend, if it cannot be avoided and the huge calculation is related to the UI, then my idea is to use setTimeout to divide the task and give a little free time in the middle Time to let the JS engine handle the UI so that the page does not freeze directly.

If you decide directly on the minimum required HTML5 version, you can take a look at the WebWorker below.

WebWorker, multi-threading of JS

As mentioned in the previous article, the JS engine is single-threaded, and if the JS execution time is too long, it will block the page, so the JS will really Is it powerless for CPU-intensive calculations?

So, Web Worker was later supported in HTML5.

MDN's official explanation is:

Web Worker provides a simple way to run scripts in a background thread for web content.

Threads can perform tasks without interfering with the user interface. A worker is an object created using a constructor (Worker()) that runs a named JavaScript file (this file contains the code that will be run in the worker thread ).

Workers run in another global context, different from the current window.

Therefore, using the window shortcut to obtain the current global scope (instead of self) will return an error within a Worker

Understand it this way:

When creating a Worker, JS The engine applies to the browser to open a sub-thread (the sub-thread is opened by the browser, is completely controlled by the main thread, and cannot operate the DOM)
The JS engine thread and the worker thread communicate through a specific method (postMessage API, which needs to be passed Serialize objects to interact with threads for specific data)
Therefore, if there is very time-consuming work, please open a separate Worker thread, so that no matter how earth-shaking it is, it will not affect the JS engine main thread, just wait for the results to be calculated Finally, just communicate the result to the main thread, perfect!

And please note that the JS engine is single-threaded. The essence of this has not changed. The Worker can be understood as a plug-in opened by the browser for the JS engine. , specially designed to solve those large computing problems.

Others, the detailed explanation of Worker is beyond the scope of this article, so I won’t go into details.

WebWorker and SharedWorker

Now that we are here, let’s mention SharedWorker again (to avoid confusing these two concepts later)

WebWorker only Belongs to a certain page and will not be shared with the Render process (browser kernel process) of other pages
So Chrome creates a new thread in the Render process (each Tab page is a Render process) to run the JavaScript in the Worker program.
SharedWorker is shared by all pages of the browser and cannot be implemented in the same way as Worker because it is not affiliated with a Render process and can be shared by multiple Render processes
Therefore, the Chrome browser creates a separate SharedWorker A process to run a JavaScript program, there is only one SharedWorker process for each same JavaScript in the browser, no matter how many times it is created.
Seeing this, it should be easy to understand. Essentially, it is the difference between processes and threads. SharedWorker is managed by an independent process, and WebWorker is just a thread under the Render process.

Browser rendering process

Supplement the browser rendering process (simple version)

In order to simplify understanding, the preliminary work is omitted directly:

Browse The browser inputs the URL, the browser main process takes over, opens a download thread, and then makes an http request (omitting DNS query, IP addressing, etc.), then waits for the response, obtains the content, and then transfers the content to the Renderer process through the RendererHost interface.
The browser rendering process begins
After the browser kernel gets the content, rendering can be roughly divided into the following steps:

Parse html and build a dom tree
Parse css and build a render tree (parse the CSS code into a tree-shaped data structure, and then combine it with the DOM to merge into a render tree)
Layout the render tree (Layout/reflow), responsible for each Calculation of element size and position
Draw render tree (paint) and draw page pixel information
The browser will send the information of each layer to the GPU, and the GPU will composite each layer and display it on the screen.
All detailed steps have been omitted. After rendering is completed, it is the load event, and then it is your own JS logic processing.

Since some detailed steps have been omitted, let me mention some details that may need attention.
Completely master the operating mechanism and principles of JavaScript

The sequence of load event and DOMContentLoaded event

As mentioned above, the load event will be triggered after rendering is completed, so can you distinguish the sequence of load event and DOMContentLoaded event? ?

It's very simple, just know their definitions:

When the DOMContentLoaded event is triggered, only when the DOM is loaded, excluding style sheets, pictures, async scripts, etc.
When the onload event is triggered, all DOM, style sheets, scripts, and images on the page have been loaded, that is, rendered.
Therefore, the order is: DOMContentLoaded -> load

Whether css loading will block dom tree rendering

What we are talking about here is the situation where css is introduced in the header

First of all, we all know that css is downloaded asynchronously by a separate download thread.

Then let’s talk about the following phenomena:

  • css loading will not block DOM tree parsing (DOM is constructed as usual during asynchronous loading)
  • But it will block render tree rendering (You need to wait until the css is loaded when rendering, because the render tree requires css information)

This may also be an optimization mechanism of the browser. Because when you load css, you may modify the style of the DOM node below. If the css loading does not block the rendering of the render tree, then after the css is loaded, the render tree may have to be redrawn or reflowed, which causes some problems. There is no necessary loss.

So just parse the structure of the DOM tree first, complete the work that can be done, and then wait for your css to be loaded, and then render the render tree according to the final style. In terms of performance, this approach It will indeed be better.

Normal layer and composite layer

The concept of composite is mentioned in the rendering step.

It can be simply understood that the layers rendered by the browser generally include two categories: ordinary layers and composite layers

First of all, the ordinary document stream can be understood as a composite layer (This is called the default composite layer. No matter how many elements are added to it, they are actually in the same composite layer)

Secondly, the absolute layout (the same is true for fixed), although it can be separated from the ordinary document flow, it Still belongs to the default composite layer.

Then, you can declare a new composite layer through hardware acceleration, which will allocate resources separately (of course it will also be separated from the ordinary document flow, so that no matter how the composite layer changes, It will not affect the reflow redraw in the default composite layer)

It can be understood simply: in the GPU, each composite layer is drawn separately, so it does not affect each other, which is why some scene hardware acceleration effects The first level is great

You can see it in Chrome DevTools --> More Tools --> Rendering --> Layer borders. The yellow one is the composite layer information

How to turn it into composite Layer (hardware acceleration)

Turn this element into a composite layer, which is the legendary hardware acceleration technology

The most commonly used method: translate3d, translateZ
opacity attribute/transition Animation (the composition layer will be created during the execution of the animation, and the element will return to its previous state after the animation has not started or ended)
will-chang attribute (this is relatively remote), generally used in conjunction with opacity and translate, its function It is to tell the browser in advance that changes are to be made, so that the browser will start to do some optimization work (it is best to release this after use)
video, iframe, canvas, webgl and other elements
Others, such as the previous flash plug-in
The difference between absolute and hardware acceleration

It can be seen that although absolute can be separated from the ordinary document flow, it cannot be separated from the default composite layer. Therefore, even if the information in absolute changes, it will not change the render tree in the ordinary document flow. However, when the browser finally draws, the entire composite layer is drawn, so changes in the information in absolute will still affect the drawing of the entire composite layer. (The browser will redraw it. If there is a lot of content in the composite layer, the drawing information brought by absolute will change too much, and the resource consumption will be very serious)

And the hardware acceleration is directly in another composite layer ( Start from scratch), so its information changes will not affect the default composite layer (of course, it will definitely affect its own composite layer internally), but only trigger the final composition (output view)

The role of composite layer

Generally, an element will become a composite layer after hardware acceleration is turned on, which can be independent of the ordinary document flow. After modification, it can avoid redrawing of the entire page and improve performance, but try to Do not use a large number of composite layers, otherwise the page will become stuck due to excessive resource consumption.

Please use index when using hardware acceleration

When using hardware acceleration, use index as much as possible to prevent By default, the browser creates composite layer rendering for subsequent elements. The specific principle is as follows: In webkit CSS3, if this element has hardware acceleration added and the index level is relatively low, then other elements behind this element will (If the level is higher than this element, or the same, and the releative or absolute attributes are the same), it will become a composite layer rendering by default. If it is not handled properly, it will greatly affect the performance.

Simple understanding, in fact, it can It is considered to be an implicit composition concept: if a is a composite layer and b is on top of a, then b will also be implicitly converted into a composite layer. This requires special attention.

Talk about the running mechanism of JS from EventLoop

At this point, it is already after the initial rendering of the browser page, and some analysis of the running mechanism of the JS engine.

Note that we do not talk about concepts such as executable context, VO, scop chain (these can be organized into another article). Here we mainly talk about how JS code is executed in combination with Event Loop.

The prerequisite for reading this part is that you already know that the JS engine is single-threaded, and several concepts mentioned above will be used here:

JS engine thread
  • Event trigger thread
  • Timed trigger thread
  • Then understand a concept:

  • JS is divided into synchronous tasks and asynchronous tasks

Synchronization Tasks are executed on the main thread, forming an execution stack
  • Outside of the main thread, the event triggering thread manages a task queue. As long as the asynchronous task has a running result, an event is placed in the task queue.
  • Once all synchronous tasks in the execution stack are executed (the JS engine is idle at this time), the system will read the task queue, add runnable asynchronous tasks to the executable stack, and start execution.
  • Look at the picture:


Completely master the operating mechanism and principles of JavaScriptAfter seeing this, you should be able to understand: Why are events pushed by setTimeout sometimes not executed on time? Because the main thread may not be idle yet and is executing other code when it is pushed into the event list, so there will naturally be errors.

The event loop mechanism is further supplemented

Completely master the operating mechanism and principles of JavaScriptThe above figure roughly describes:

The main thread will generate an execution stack when it is running. When the code in the stack calls certain APIs, they will add various events to the event queue (when the trigger conditions are met, such as the ajax request is completed)
  • When the code in the stack is executed, it will be read Events in the event queue, execute those callbacks, and so on
  • Note that you always have to wait for the code in the stack to complete before reading the events in the event queue

Let’s talk about timers separately

The core of the above event loop mechanism is: JS engine thread and event trigger thread

But there are some hidden details in the event, for example, after calling setTimeout, How to wait for a specific time before adding to the event queue?

Is it detected by the JS engine? Of course not. It is controlled by the timer thread (because the JS engine itself is too busy and has no time to spare)

Why do we need a separate timer thread? Because the JavaScript engine is single-threaded, if it is in a blocked thread state, it will affect the accuracy of timing, so it is necessary to open a separate thread for timing.

When will the timer thread be used? When using setTimeout or setInterval, it requires the timer thread to time, and after the time is completed, the specific event will be pushed into the event queue.

setTimeout instead of setInterval

There is a difference between using setTimeout to simulate regular timing and using setInterval directly.

Because each time setTimeout is executed, it will be executed, and then setTimeout will continue after a period of execution. There will be errors in the middle (the error is related to the code execution time)

And setInterval It is to push an event accurately every time. However, the actual execution time of the event may not be accurate. It is possible that the next event will come before the event is completed.

And setInterval has some fatal problems:

Cumulative effect, if the setInterval code has not completed execution before being added to the queue again, it will cause the timer code to run several times continuously without any interval. Even if they are executed at normal intervals, the execution time of multiple setInterval codes may be shorter than expected (because the code execution takes a certain amount of time)
For example, browsers such as iOS webview or Safari have a feature that when scrolling If you do not execute JS, if you use setInterval, you will find that it will be executed multiple times after the scrolling. Because the scrolling does not execute JS, the accumulated callbacks will cause the container to freeze and cause some unknown errors if the callback execution time is too long ( This section will be supplemented later. SetInterval’s built-in optimization will not add callbacks repeatedly)
And when the browser is minimized and displayed, setInterval does not not execute the program, it will put the callback function of setInterval in the queue. , when the browser window is opened again, it will all be executed in an instant
Therefore, in view of so many problems, the best solution currently generally considered is: use setTimeout to simulate setInterval, or directly use requestAnimationFrame

for special occasions. : As mentioned in JS Elevation, the JS engine will optimize setInterval. If there is a setInterval callback in the current event queue, it will not be added repeatedly.

Advanced event loop: macrotask and microtask

The above has sorted out the JS event loop mechanism, which is sufficient in the case of ES5 , but now that ES6 is prevalent, you will still encounter some problems, such as the following question:

console.log('script start');

setTimeout(function() {
    console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
    console.log('promise1');
}).then(function() {
    console.log('promise2');
});

console.log('script end');

Hmm, the correct execution order is like this:

script start
script end
promise1
promise2
setTimeout

Why? ? Because there is a new concept in Promise: microtask

Or, further, JS is divided into two task types: macrotask and microtask. In ECMAScript, microtask is called jobs, and macrotask can be called task.

What are their definitions? the difference? To put it simply, it can be understood as follows:

1. macrotask (also called macro task)

It can be understood that the code executed by the execution stack each time is a macro task (including each time from the event Obtain an event callback from the queue and put it into the execution stack for execution)

  • Each task will execute the task from beginning to end and will not execute other
  • In order to be able This enables JS internal tasks and DOM tasks to be executed in an orderly manner. After the execution of one task ends, the page will be re-rendered before the execution of the next task starts (task -> rendering -> task -> ...)
2. Microtask (also known as microtask)

It can be understood that it is a task that is executed immediately after the execution of the current task ends

  • In other words, after the current task, before the next task, and before rendering
  • , its response speed will be faster than setTimeout (setTimeout is a task), because there is no need to wait for rendering
  • That is to say, after a certain macrotask is executed, all microtasks generated during its execution will be executed (before rendering)
3. What kind of scenarios will Forming macrotask and microtask
  • macrotask: main code block, setTimeout, setInterval, etc. (each event in the event queue is a macrotask)
  • microtask: Promise, process.nextTick Wait

Addition: In the node environment, process.nextTick has a higher priority than Promise, which can be simply understood as: after the macro task ends, the nextTickQueue part of the microtask queue will be executed first. Then the Promise part of the microtask will be executed.

Let’s understand it based on threads:

(1) Events in macrotask are placed in an event queue, and this queue is maintained by the event triggering thread

(2) All microtasks in the microtask are added to the microtask queue (Job Queues), waiting for execution after the current macrotask is completed, and this queue is maintained by the JS engine thread (this is inferred from my own understanding, because it It is executed seamlessly under the main thread)
So, to summarize the operating mechanism:

  • Execute a macro task (if it is not on the stack, get it from the event queue)
  • If a microtask is encountered during execution, add it to the task queue of the microtask
  • After the macrotask is executed, all microtasks in the current microtask queue will be executed immediately (executed in sequence)
  • After the current macro task is executed, the rendering starts to be checked, and then the GUI thread takes over the rendering
  • After the rendering is completed, the JS thread continues to take over and starts the next macro task (obtained from the event queue)

As shown in the picture:
Completely master the operating mechanism and principles of JavaScript

In addition, please pay attention to the difference between the Promise polyfill and the official version:

In the official version, it is the standard microtask form
Polyfill is generally simulated through setTimeout, so it is in the form of macrotask
Note that some browsers have different execution results (because they may execute microtask as macrotask), but for the sake of simplicity, some differences will not be described here. Scenario under standard browsers (but remember, some browsers may not be standard)

补充:使用MutationObserver实现microtask

MutationObserver可以用来实现microtask (它属于microtask,优先级小于Promise, 一般是Promise不支持时才会这样做)

它是HTML5中的新特性,作用是:监听一个DOM变动, 当DOM对象树发生任何变动时,Mutation Observer会得到通知

像以前的Vue源码中就是利用它来模拟nextTick的, 具体原理是,创建一个TextNode并监听内容变化, 然后要nextTick的时候去改一下这个节点的文本内容, 如下:

var counter = 1
var observer = new MutationObserver(nextTickHandler)
var textNode = document.createTextNode(String(counter))

observer.observe(textNode, {
    characterData: true
})
timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
}

不过,现在的Vue(2.5+)的nextTick实现移除了MutationObserver的方式(据说是兼容性原因), 取而代之的是使用MessageChannel (当然,默认情况仍然是Promise,不支持才兼容的)。

MessageChannel属于宏任务,优先级是:MessageChannel->setTimeout, 所以Vue(2.5+)内部的nextTick与2.4及之前的实现是不一样的,需要注意下。

【相关推荐:javascript视频教程web前端

The above is the detailed content of Completely master the operating mechanism and principles of JavaScript. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:csdn.net. If there is any infringement, please contact admin@php.cn delete