preface
Limited opinions, if there are improper description, please help to point out in time, if there are mistakes, will be timely correction.
———- super long + multi-picture warning, need to spend a lot of time. ———-
If you read this article, but also to process threads silly confused, not clear browser multi-process, browser kernel multithreading, JS single thread, JS running mechanism difference. Then please reply me, it must be my writing is not clear enough, I will change…
———- Start the text ———-
Recently, I found that there are many articles about the JS single thread running mechanism, but I found that many of them are only to introduce a certain part of the knowledge, and the various places are not unified, easy to cause confusion. Therefore, I am going to comb this knowledge point, combine the existing cognition, based on a large number of reference materials on the Internet, from browser multi-process to JS single-thread, and comb the running mechanism system of JS engine again.
Form of presentation: Since it belongs to systematic sorting, it does not start from the shallow to the deep, but to sort out the knowledge system from beginning to end, focusing on connecting the knowledge points of key nodes, rather than just analyzing a certain part of the knowledge.
The content is: from the browser process, then to the browser kernel running, then to the JS engine single thread, and then to the JS event loop mechanism, from the beginning to the end of the system comb, get rid of fragmentation, form a knowledge system
The goal is: after reading this article, you can have a certain understanding of the browser multi-process, JS single thread, JS event loop mechanism and so on, and have a skeleton of knowledge, rather than a sense of half-comprehension.
In addition, this article is suitable for front-end personnel with certain experience, novice please avoid, avoid too much concept impact. It can be stored first and watched after a certain understanding, or it can be divided into multiple batches to avoid excessive fatigue.
The outline
-
Distinguish between processes and threads
-
Browsers are multi-process
-
What processes are included in the browser?
-
Multi-process advantages of browsers
-
The focus is on the browser kernel (rendering process)
-
The Browser process communicates with the Browser kernel (Renderer process)
-
-
Comb through the relationships between threads in the browser kernel
-
The GUI rendering thread is mutually exclusive with the JS engine thread
-
JS blocks page loading
-
WebWorker, JS multithreading?
-
WebWorker and SharedWorker
-
-
Take a quick look at the browser rendering process
-
The sequence of the load event and the DOMContentLoaded event
-
Does CSS loading block DOM tree rendering?
-
Regular layers and composite layers
-
-
Talk about JS operation mechanism from Event Loop
-
Event loops are further complemented
-
Talk about timers alone
-
SetTimeout instead of setInterval
-
-
Event loop progression: MacroTask and MicroTask
-
In the last words
Distinguish between processes and threads
Not distinguishing between threads and processes is a common mistake many novices make, and it’s ok. That’s normal. Let’s start with this graphic metaphor:
- A process is a factory with independent resources - factories are independent of each other - threads are workers in the factory, multiple workers collaborate to complete a task - one or more workers in the factory - workers share spaceCopy the code
To refine the concept:
- Factory resources -> system allocated memory (separate block of memory) - independent between factories -> independent between processes - multiple workers collaborate to complete a task -> Multiple threads collaborate in a process - One or more workers in a factory -> a process consists of one or more threads - Shared space between workers -> Shared memory space between threads in the same process (including code segments, data sets, heap, etc.)Copy the code
And then to consolidate:
On a Windows computer, you can open task Manager and see a list of background processes. Yes, that’s where you can view the processes, and you can see the memory resources and CPU usage for each process.
So, it should be easier to understand: a process is the smallest unit of CPU resources allocated (the system allocates memory to it)
And finally, in more official terms:
-
A process is the smallest unit of CPU resources allocated (the smallest unit that can hold resources and run independently).
-
A thread is the minimum unit of CPU scheduling. (A thread is a unit of program running at one time based on a process. A process can have multiple threads.)
tips
-
Different processes can also communicate with each other, but at a higher cost
-
Today, the common terms: single-threaded and multi-threaded refer to single and many within a process. (So the core still has to belong to a process)
Browsers are multi-process
Now that you understand the difference between a process and a thread, it’s time to get some perspective on the browser:
-
Browsers are multi-process
-
The browser works because the system allocates resources (CPU, memory) to its processes
-
To keep it simple, every time you open a Tab, you create a separate browser process.
For verification of the above points, please refer to the first picture:
In this image, multiple tabs of the Chrome browser are opened, and you can see multiple processes in Chrome’s Task Manager (one separate process for each Tab and one main process). If you are interested in it, you can try it yourself. If you open another Tab page, the process will normally +1
** Note: ** the browser should also have its own optimization mechanism at this point. Sometimes when you open multiple Tabs, you can see in the Chrome Task Manager that some processes are merged (so one process per TAB may not be absolute).
What processes are included in the browser?
Now that you know the browser is multi-process, let’s take a look at exactly what processes it contains :(just list the main processes for simplicity)
-
Browser process: The main process of the Browser (responsible for coordination, control), only one. There are
-
Responsible for browser interface display and user interaction. Advance, retreat, etc
-
Responsible for managing individual pages, creating and destroying other processes
-
Draws an in-memory Bitmap from the Renderer process to the user interface
-
Network resource management, download and so on
-
-
Third-party plug-in process: Each type of plug-in corresponds to a process that is created only when the plug-in is used
-
GPU process: at most one GPU process, used for 3D rendering, etc
-
Browser rendering process (browser kernel) (Renderer process, internal is multi-threaded) : defaults to one process per Tab page and does not affect each other. The main function is
- Page rendering, script execution, event handling, etc
Memory enhancement: Opening a web page in a browser is equivalent to opening a new process (processes have their own multiple threads)
Of course, browsers sometimes merge multiple processes (for example, when you open multiple blank tabs, you find multiple blank tabs merged into a single process), as shown in the figure below
In addition, you can verify yourself through Chrome’s more tools -> Task Manager
Multi-process advantages of browsers
Compared to a single-process browser, multi-process has the following advantages:
-
Avoid a single page crash affecting the entire browser
-
Prevent third-party plug-in crash from affecting the entire browser
-
Multi-process makes full use of multi-nuclear advantages
-
Easy to use sandbox model to isolate processes such as plug-ins, improving browser stability
To keep it simple: if the browser is a single process, and one Tab crashes, it affects the entire browser, how bad the experience is; If it is a single process, a plugin crash will affect the entire browser; And there are many other advantages to multiple processes…
Of course, memory and other resource consumption will also be larger, a bit of space for time meaning.
The focus is on the browser kernel (rendering process)
Now, as you can see, with so many processes mentioned above, what is the ultimate requirement for normal front-end operations? The answer is the rendering process
It can be understood that page rendering, JS execution, event loop, all in this process. Let’s focus on this process
Keep in mind that the browser’s rendering process is multithreaded (if this doesn’t make sense, go back to the process vs. thread distinction)
Finally arrived the thread this concept ðŸ˜, good kind. Let’s take a look at what threads it contains (to list some of the main resident threads) :
-
GUI rendering thread
-
Responsible for rendering the browser interface, parsing HTML, CSS, building DOM and RenderObject trees, layout and rendering, etc.
-
This thread executes when the interface needs to be repainted or when some operation causes reflow
-
Note that the GUI rendering thread is mutually exclusive with the JS engine thread, the GUI thread is suspended while the JS engine is executing, and GUI updates are stored in a queue until the JS engine is idle and executed immediately.
-
-
JS engine thread
-
Also known as the JS kernel, it handles Javascript scripts. (e.g. V8 engine)
-
The JS engine thread is responsible for parsing the Javascript script and running the code.
-
The JS engine waits for a task to arrive in the task queue and then processes it. There is only one JS thread running the JS program in a Tab (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 takes too long, the rendering of the page will be incoherent and the page rendering load will be blocked.
-
-
Event-triggered thread
-
It belongs to the browser, not the JS engine, and is used to control the event loop.
-
When the JS engine executes a code block such as setTimeOut (or from other threads in the browser kernel, such as mouse clicks, AJAX asynchronous requests, etc.), it adds the corresponding task to the event thread
-
When the corresponding event is triggered according to the triggering condition, the thread will add the event to the end of the queue to be processed, waiting for the JS engine to process
-
Note that due to the single-threaded nature of JS, the events in the queue are queued for the JS engine to process (only when the JS engine is idle).
-
-
Timed trigger thread
-
The thread where the legendary setInterval and setTimeout reside
-
The browser timing counter is not counted by the JavaScript engine (because the JavaScript engine is single-threaded, if you are in a blocked thread state, it will affect the accuracy of the timing)
-
Therefore, the timer is timed and triggered by a separate thread (after the timer is finished, it is added to the event queue, waiting for the JS engine to idle).
-
Note that the W3C states in the HTML standard that any interval of less than 4ms in setTimeout is required to count as 4ms.
-
-
Asynchronous HTTP request threads
-
After the XMLHttpRequest is connected, it opens a new thread request through the browser
-
When a state change is detected and a callback function is set, the asynchronous thread generates a state change event and places the callback in the event queue. This is then executed by the JavaScript engine.
-
If you’re tired, take a break. These concepts need to be digested. After all, the event loop that will be mentioned later is based on event-triggered threads, so just looking at a fragment of knowledge can be a bit confusing. To complete the comb can quickly precipitate, not easy to forget. Here’s a picture to reinforce:
One more thing, why is the JS engine single threaded? Well, there should be no standard answer to this question, for example, it could simply be that due to the complexity of multithreading, for example, multi-threaded operations generally require locking, so the initial design was single-threaded…
The Browser process communicates with the Browser kernel (Renderer process)
The Browser process (the control process) communicates with the kernel. After that, you can put this part of the knowledge together and have a complete concept from beginning to end.
If you open the Task Manager and open a browser, you can see that there are two processes in the task Manager (one is the master process and the other is the Tab rendering process), then you can see the whole process: (simplified)
-
The Browser process receives the user request, first needs to obtain the page content (such as downloading resources through the network), and then passes the task to the Render process through the RendererHost interface
-
The Renderer interface of the Renderer process receives the message, briefly interprets it, gives it to the Renderer thread, and then starts rendering
-
The render thread receives the request, loads the page, and renders the page, which may require the Browser process to retrieve resources and the GPU process to help render
-
Of course, it is possible for JS threads to manipulate the DOM (this may cause a backflow redraw).
-
Finally, the Render process passes the result to the Browser process
-
-
The Browser process receives the results and draws them
Here’s a simple diagram :(very simplified)
After looking at this whole process, you should have a certain understanding of the operation of the browser, so that you have the basis of the knowledge structure, and then it is convenient to fill in the content.
This further talk involves the browser kernel source code parsing, beyond the scope of this article.
If you want to dig deeper in this area, I recommend reading some browser kernel source parsing articles, or you can check out the first article in the reference source, which is well written
Comb through the relationships between threads in the browser kernel
Now that you have a general idea of how the browser works, let’s take a look at some of the concepts
The GUI rendering thread is mutually exclusive with the JS engine thread
Since JavaScript manipulates the DOM, if the interface is rendered while modifying these element attributes (i.e., the JS thread and UI thread are running simultaneously), then the element data obtained by the rendering thread may be inconsistent.
Therefore, in order to prevent unexpected results from rendering, the browser sets the GUI rendering thread to be mutually exclusive with the JS engine. When the JS engine executes, the GUI thread will be suspended, and GUI updates will be stored in a queue until the JS engine thread is idle and executed immediately.
JS blocks page loading
From this mutual exclusion, it follows that JS will block pages if it takes too long to execute.
For example, if the JS engine is doing a huge amount of computation, even if there is an update to the GUI, it will be queued for execution when the JS engine is idle. Then, due to the huge amount of computation, so the JS engine is likely to be idle for a long time, naturally will feel huge card.
Therefore, try to avoid the JS execution time is too long, which will cause the page rendering incoherent, resulting in the page rendering load blocking feeling.
WebWorker, JS multithreading?
As mentioned earlier, the JS engine is single-threaded and takes too long to execute to block the page, so is JS really incapable of CPU intensive computation?
So Web workers were later supported in HTML5.
The official MDN explanation is:
Web Worker provides an easy way for Web content to run scripts in background threads. Threads can perform tasks without interfering with the user interface. A worker is an object created using a constructor (e.g. worker ()) that runs a named JavaScript file that contains the code to be run in the worker thread; Workers run in another global context, different from the current onewindowTherefore, usingwindowA shortcut to get the current global scope (not self) in a Worker will return an errorCopy the code
Think of it this way:
-
When the Worker is created, the JS engine asks the browser to open a child thread (the child thread is opened by the browser, completely controlled by the main thread, and it cannot operate the DOM).
-
JS engine threads communicate with worker threads in a specific way (postMessage API, which requires serializing objects to interact with threads with specific data)
Therefore, if there is very time-consuming work, please open a separate Worker thread, so that no matter how earthshaking it is, it will not affect the main thread of JS engine. After the result is calculated, the result can be communicated to the main thread. Perfect!
Also note that the JS engine is single-threaded, the essence of which is still the same, and the Worker can understand that the browser is plug-in to the JS engine to solve a lot of computing problems.
In addition, detailed explanation of Worker is not the scope of this article, so no further details.
WebWorker and SharedWorker
Now that we’re there, let’s mention SharedWorker again (to avoid confusing the two concepts later)
-
The WebWorker belongs only to one page and is not shared with the Render process (browser kernel process) of other pages
- So Chrome creates a new thread in the Render process (every Tab page is a Render process) to run the JavaScript program in the Worker.
-
SharedWorker is shared by all pages of the browser and cannot be implemented in the same way as Worker, because it is not affiliated to a certain Render process and can be shared by multiple Render processes
- So Chrome creates a separate process for SharedWorker to run the JavaScript program. There is only one SharedWorker process for each identical JavaScript in the browser, no matter how many times it is created.
Now, it should be easy to see the difference between processes and threads. SharedWorker is managed by a separate process, while WebWorker is just a thread belonging to the Render process
Take a quick look at the browser rendering process
Originally, I planned to start talking about the JS mechanism, but I thought it would be awkward to skip to JS since I have been talking about the browser, so I will add the browser rendering process in the middle (simple version).
In order to simplify the understanding, the preliminary work is directly omitted :(to be developed or completely can write another super long article)
- The browser enters the URL, the browser main process takes over, opens a download thread, makes an HTTP request (skipping DNS queries, IP addressing, etc.), waits for the response, retrives the content, and then passes the content to the Renderer process via the RendererHost interface - the browser rendering process beginsCopy the code
Once the browser kernel has the content, rendering can be divided into the following steps:
-
Parse HTML to build a DOM tree
-
Parse CSS code into a tree-shaped data structure, and then combine it with DOM to form a Render tree.
-
Layout render tree (Layout/reflow), responsible for the calculation of the size and position of each element
-
Render tree (paint), drawing page pixel information
-
The browser will send the information of each layer to the GPU, and the GPU will composite the layers and display them on the screen.
All the detailed steps have been omitted. After rendering, there is the load event, and then it is handled by your own JS logic
Since some of the detailed steps have been omitted, let’s mention some of the details that may need attention.
Here’s a redrawing of one of the references :(reference # 1)
The sequence of the load event and the DOMContentLoaded event
As mentioned above, the load event is triggered when the render is complete. Can you tell the load event from the DOMContentLoaded event?
It’s easy, just know the definition:
- When the DOMContentLoaded event is triggered, it is only when the DOM has been loaded and does not include stylesheets or images.
(For example, if there is an async loaded script, it may not complete)
- When the onLoad event is triggered, all the DOM, stylesheets, scripts, and images on the page have been loaded.
(Finished rendering)
So, the order is: DOMContentLoaded -> Load
Does CSS loading block DOM tree rendering?
This is about introducing CSS to the header
First, we all know that CSS is downloaded asynchronously by a separate download thread.
Then there are the following phenomena:
-
CSS loading does not block DOM tree parsing (DOM is built as usual for asynchronous loading)
-
But it blocks the render tree until the CSS is loaded, because the Render tree needs the CSS information.
This may also be an optimization mechanism for browsers.
If the CSS loading does not block the render tree, then the Render tree may have to be redrawn or reflow after the CSS loading, causing unnecessary loss. Therefore, simply parse the structure of the DOM tree first, finish the work that can be done, and then wait for your CSS to load, in accordance with the final style to render the tree, this approach will indeed be better performance.
Regular layers and composite layers
The concept of composite is mentioned in the render step.
The simple way to think about it is that there are two main types of layers that browsers render: normal layers and composite layers
First, the normal document flow can be thought of as a composite layer (here called the default composite layer, no matter how many elements are added to the composite layer).
Second, absolute layout (as well as Fixed) is still the default composition layer, although it can be detached from the normal document flow.
Then, with hardware acceleration, you can declare a new composite layer that will allocate resources separately (and out of the normal document flow, of course, so that whatever changes are made in this composite layer will not affect the backflow redraw in the default composite layer).
On the GPU, each composite layer is drawn separately, so it does not affect each other. This is why some scenes have excellent hardware acceleration
You can see in Chrome -> More Tools -> Rendering -> Layer Borders that the yellow color is the composite Layer information
The diagram below. Can verify the above statement
How to Become a Composite Layer (hardware Accelerated)
Turning this element into a composite layer is known as hardware acceleration
-
Most commonly used: Translate3D, translateZ
-
(The composition layer is created only during the execution of the animation, and the element returns to its previous state after the animation does not start or end)
-
The will-chang property (this one is remote), which is normally used with opacity and Translate (and tests show that the other properties, except the one above that triggers hardware acceleration, do not become the composite layer),
The function is to tell the browser ahead of time that the change is coming, so the browser can start to do some optimization work (this is best released after it’s finished).
-
< video > < iframe > < canvas > < webgl > element
-
Others, such as the old Flash plug-in
Absolute and hardware acceleration
As you can see, Absolute can be detached from the normal document flow, but it cannot be detached from the default composition layer. Therefore, even if the information in Absolute does not change the render tree in the ordinary document flow, the browser finally draws the entire composite layer, so the change of information in Absolute will still affect the rendering of the entire composite layer. (The browser will redraw it. If there is too much content in the compound layer, the drawing information changes too much, which is very serious resource consumption.)
Hardware acceleration is directly in another composite layer, so its information changes do not affect the default composite layer (of course, the internal will definitely affect its own composite layer), but only trigger the final composite (output view).
What do composite layers do?
Usually an element with hardware acceleration turned on becomes a composite layer, which can be separated from the normal document flow and can be changed to avoid redrawing the entire page and improve performance
But try not to use a lot of composite layers, otherwise due to excessive consumption of resources, the page will become more stuck
For hardware acceleration, use index
When using hardware acceleration, use index whenever possible to prevent the browser from creating composite renderings for subsequent elements by default
The specific principle is as follows: In WebKit CSS3, if the element has hardware acceleration and the index level is low, then all the following elements (higher or the same level, and the same releative or absolute attribute) will be rendered in the composite layer by default. If not handled properly, performance can be significantly affected
If A is a composite layer and B is on top of A, then B is also implicitly converted to a composite layer
In addition, the problem can be reproduced at this address:
web.jobbole.com/83575/
Talk about JS operation mechanism from Event Loop
At this point, is already belong to the browser page after the first rendering things, JS engine some operation mechanism analysis.
Note that instead of talking about executable context, VO, scop chain, etc. (which could be a whole other article), we’ll talk about how JS code executes in conjunction with Event Loop.
The JS engine is single-threaded, and several concepts from the previous section will be used.
-
JS engine thread
-
Event-triggered thread
-
Timed trigger thread
And 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 and places an event in the task queue whenever the asynchronous task has run results.
-
Once all the synchronous tasks in the execution stack are finished (at this time the JS engine is idle), the system will read the task queue, add the running asynchronous tasks to the execution stack, and start to execute.
Look at the picture:
This should make sense: Why sometimes events pushed by setTimeout don’t execute on time? It is possible that the main thread is not idle and executing other code when it is pushed into the event list, so there is an error.
Event loops are further complemented
Here’s a direct quote to Help :(from Philip Roberts’ talk, Help, I’m stuck in an event-loop)
Here’s a rough picture:
- When the main thread runs, it produces an execution stack,
When code in the stack calls certain apis, they add various events to the event queue (when triggering conditions are met, such as the completion of an Ajax request)
-
When the code in the stack finishes executing, it reads the events in the event queue to perform those callbacks
-
The cycle
-
Note that events in the event queue are always read until the code in the stack is finished executing
Talk about timers alone
The core of the above event loop mechanism is: JS engine thread and event trigger thread
But on events, there are some hidden details, such as how after calling setTimeout, it waits for a certain amount of time to be added 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 to do anything).
Why a separate timer thread? Because the JavaScript engine is single-threaded, it is important to keep a separate thread open for timing because it is not accurate if you are in a blocked thread state.
When will the timer thread be used? When a setTimeout or setInterval is used, it requires the timer thread to time, and when the timer is finished, the specific event is pushed into the event queue.
Such as:
setTimeout(function(){
console.log('hello! ');
}, 1000);
Copy the code
The purpose of this code is to push the callback function into the event queue and wait for the main thread to execute when the 1000ms timer is finished (timed by the timer thread)
setTimeout(function(){
console.log('hello! ');
}, 0);
console.log('begin');
Copy the code
The effect of this code is to push the callback function into the event queue for the main thread to execute as quickly as possible
Note:
-
The result is: begin and hello!
-
Although the code is meant to push the event queue after 0 milliseconds, the W3C in the HTML standard requires that any interval of less than 4ms in setTimeout be counted as 4ms.
(However, it is also said that different browsers have different minimum time Settings)
- Even if it doesn’t wait 4ms, even if it is pushed to the event queue at 0 ms, it will execute first
begin
(Because the event queue is actively read only when the executable stack is empty)
SetTimeout instead of setInterval
There is a difference between simulating periodic timing with setTimeout and using setInterval directly.
Because every time a setTimeout is timed, it’s going to execute it, and then it’s going to execute it for a certain amount of time before it’s going to resume the setTimeout, so it’s going to have an error in between.
SetInterval, on the other hand, pushes in an event at a precise interval each time (however, the actual execution time of the event is not necessarily accurate, and it may be that the next event comes before the event has completed).
And there are some deadly problems with setInterval:
- Cumulative effect (mentioned above), if the setInterval code has not completed execution before (setInterval) is added to the queue again,
This causes the timer code to run several times in a row with no interval between them. Even with normal intervals, multiple setIntervals may take less time to execute than expected (because code takes time to execute).
-
For example, if you look at webView in iOS, or Safari or whatever, there’s a feature where you don’t do JS while you’re scrolling, and if you use setInterval, you’ll notice that it’s going to be done multiple times after the scrolling is over because you don’t do JS save callbacks for scrolling, If the callback takes too long to execute, it can cause lag problems and unexpected errors. (This will be added later. SetInterval is optimized to not add callbacks repeatedly.)
-
And instead of not executing the program when you minimize the browser display and so on,
It will queue the callback function for the setInterval, and when the browser window opens again, all of it will be executed in a flash
So, given all these problems, it’s generally accepted that the best solution is to emulate setInterval with setTimeout, or in special cases use requestAnimationFrame directly
Add-on: as mentioned in the JS elevation, the JS engine will optimize the setInterval so that if the current event queue has a setInterval callback, it will not be added again. Still, there are a lot of problems…
Event loop progression: MacroTask and MicroTask
This paragraph refers to the second article (English version) in the reference source, (add their own understanding to describe the next), strongly recommend the students who have English foundation to directly watch the original text, the author describes very clearly, the example is also very good, as follows:
Jakearchibald.com/2015/tasks-…
In the case of ES5, the JS event loop mechanism is sufficient, but in the current ES6, there are still some problems, such as the following problem:
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');
Copy the code
Well, the correct order of execution looks like this:
script start
script end
promise1
promise2
setTimeout
Copy the code
Why is that? Because there was a new concept in the Promise: MicroTask
Or, further, there are two types of tasks in JS: MacroTask and MicroTask. In ECMAScript, microTask is called jobs and macroTask is called Task
What are their definitions? The difference between them? Simple point can be understood as follows:
-
Macrotask (also known as macrotask), it can be understood that the code executed on each execution stack is a macrotask (including getting an event callback from the event queue and putting it on the execution stack each time).
-
Each task executes this task from beginning to end, and nothing else
-
In order to enable JS internal tasks and DOM tasks to be executed in an orderly manner, the browser will re-render the page after the execution of one task before the execution of the next task
(task-> render ->task->…)
-
-
A microtask (also known as a microtask) is understood to be a task that is executed immediately after the execution of the current task
-
That is, after the current task, before the next task, before rendering
-
So it will respond faster than setTimeout (which is task) because you don’t have to wait to render
-
That is, after a macroTask is executed, all microtasks generated during its execution are executed (before rendering).
-
What is the difference between macroTask and MicroTask?
-
Macrotask: main code block, setTimeout, setInterval, etc. (As you can see, each event in the event queue is a MacroTask)
-
Microtask: Promise, process. NextTick, etc
In the node environment, the priority of process.nextTick is higher than that of Promise, which simply means that the nextTickQueue part of the microtask queue is executed after the macro task is finished, and then the Promise part of the microtask is executed.
Reference: segmentfault.com/q/101000001…
Let’s think about it in terms of threads:
-
The events in a MacroTask are placed in an event queue maintained by the event triggering thread
-
All microtasks in microTask are added to Job Queues, which are maintained by JS engine threads, for execution when the current MacroTask has completed
(This is understood + inferred, as it is executed seamlessly under the main thread)
So, to summarize the mechanism:
-
Execute a macro task (if not in the stack, get from the event queue)
-
If a microtask is encountered during execution, it is added to the task queue of the microtask
-
All microtasks in the current microtask queue are executed immediately after the macro task is executed (in sequence).
-
The current macro task is finished, the rendering is checked, and the GUI thread takes over the rendering
-
After rendering, the JS thread continues to take over and starts the next macro task (fetch from the event queue)
As shown in figure:
Also, note the difference between Promise’s Polyfill and the official version:
-
In the official version, this is the standard form of MicroTask
-
Polyfill, which is typically simulated with setTimeout, is macroTask form
-
Please pay special attention to these two differences
Note that some browsers execute differently (because they may execute microTask as macroTask), but for the sake of simplicity, I won’t describe scenarios in non-standard browsers (but remember that some browsers may not be standard).
20180126 Supplement: Implement MicroTask using MutationObserver
MutationObserver can be used to implement microTasks (it is a microTask and has a smaller priority than a Promise, usually when the Promise does not support it)
It is a new feature in HTML5 that listens for changes to a DOM and the Mutation Observer is notified of any changes to the DOM object tree
Create a TextNode and listen for changes in the content. Then change the text content of the node as follows :(Vue source code, unchanged)
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)
}
Copy the code
Corresponding Vue source link
However, the current Vue (2.5+) nextTick implementation removes the MutationObserver approach (reportedly for compatibility reasons) and instead uses a MessageChannel (of course, the default is still a Promise and compatibility is not supported).
MessageChannel is a macro task with the priority of MessageChannel->setTimeout, so the nextTick in Vue (2.5+) is different from that in 2.4 and earlier implementations.
Instead of expanding it here, you can look at juejin.cn/post/684490…
In the last words
See here, do not know to JS operation mechanism is more understanding, comb from beginning to end, rather than a certain fragment of knowledge should be clearer?
At the same time, it should also be noted that JS is not as simple as imagined, the front-end knowledge is endless, endless concepts, N easy to forget knowledge points, a variety of frameworks, the underlying principles can be infinitely deep dig, and then you will find that you know too little…
In addition, this article is going to end first, other concepts, such as JS lexical parsing, executable context and VO, will not continue to write in this article, and can be considered to open a new article.
Finally, if you like it, please give a like!
The appendix
blog
It was first published on my personal blog on 01.21, 2018
www.dailichun.com/2018/01/21/…
Push email
Resume sent to my email, there will be a response, in line with the requirements of direct go inside push!!
One to one service, any questions must answer!
You can also add me on wechat: A546684355
The resources
-
www.cnblogs.com/lhb25/p/how…
-
Jakearchibald.com/2015/tasks-…
-
Segmentfault.com/p/121000001…
-
Blog.csdn.net/u013510838/…
-
Blog.csdn.net/Steward2011…
-
www.imweb.io/topic/58e3b…
-
Segmentfault.com/a/119000000…
-
Juejin. Cn/post / 684490…
-
www.cnblogs.com/iovec/p/790…
-
www.cnblogs.com/wyaocn/p/57…
-
www.ruanyifeng.com/blog/2014/1…