This article is designed to deepen my understanding of browser processes, JS event loops, macro tasks and micro tasks

Distinguish between processes and threads

To use a vivid metaphor:

  • A process is a factory that has its own independent resources

    • Factories are independent of each other -> processes are independent of each other
    • One or more workers in a factory one > process consists of one or more threads
    • Shared space between workers -> The memory space of a program (such as code snippet, data set, etc.) shared between threads of the same process
  • Threads are workers in a factory where multiple workers collaborate to complete a task -> multiple threads collaborate with each other in a process to complete a task

Browsers are multi-process

  • The Browser process, the main process of the Browser, has only one function:

    • Responsible for browser interface display and user interaction. Forward, backward, etc
    • Responsible for page management, create and destroy other processes
    • Draw the Renderer process into the user interface
    • Network resource management, download, etc
  • Third-party plug-in process: Each type of plug-in corresponds to one process, which is created only when the plug-in is used

  • GPU process: A graphics processing unit (GPU) process that displays a maximum of one UI for 3D drawing

  • Renderer: the browser Renderer process is used for page rendering, script execution, event handling, etc. ** Opening a web page in the browser is equivalent to creating a new process (the process has its own multithreading), or multiple processes can be merged into one (important);

Advantages of multi-process browsers

  • Avoid a single page crash affecting the entire browser
  • Avoid third-party plug-in crash affecting the entire browser
  • Multi-process makes full use of multi-nuclear advantages
  • It is convenient to use sandbox model to isolate processes such as plug-ins and improve browser stability
Simple: If the browser is single-process, a Tab page or plug-in crashes, affecting the entire browserCopy the code

Key, browser kernel (renderer)

For the front end, page rendering, JS execution, and event loops are all done in this process. Browser renderers are multithreaded. What threads are included in the browser’s rendering process:

  1. GUI rendering thread
    • Responsible for rendering browser interface, parsing HTML, CSS, building DOM Tree, CSS Tree and RenderObject Tree, layout and drawing
    • This thread executes when the interface needs to be repainted or when some operation causes reflow
    • The GUI rendering thread and the JS engine thread are mutually exclusive. The GUI thread is suspended (frozen) while the JS engine is executing, and GUI updates are stored in a queue and executed as soon as the JS engine is idle.
  2. JS engine thread, single thread
    • Also known as the JS kernel, responsible for handling JS scripts. Like the V8 engine
    • Is there only one thread of the JS engine running on a Tab page (Renderer process) at any given time
    • The GUI rendering thread and the JS engine thread are mutually exclusive, so if JS execution takes too long, the page rendering will be incoherent.
  3. Event trigger thread
    • The browser, not the JS engine, is used to control the event loop.
    • When the JS engine executes a code block and setTimeout (or other threads from the browser kernel, such as mouse clicks, Ajax asynchronous requests, etc.), it adds the corresponding event task to the event thread (event queue)
    • When the corresponding event meets the trigger condition is triggered, the thread will add the event to the tail of the queue to be processed, waiting for the JS engine to process
    • Since JS is single-threaded, the events in the queue must be queued up for the JS engine to process (when the JS engine is idle).
  4. Timing trigger thread
    • setIntervalandSetTimeout Specifies the thread of the setTimeout
    • Browser timing counters are not counted by the JS engine, because the JS engine is single-threaded and blocking threads can affect timing accuracy
    • Therefore, the timing trigger thread is used to time and trigger the timing. After the timing is finished, it is added to the event queue and wait for the JS engine to be idle
    • In the HTML standard, the W3C requires that setTimeout intervals less than 4ms count as 4ms
  5. Asynchronous HTTP request threads
    • XMLHttpRequest opens a new thread request in the browser after the connection
    • When a state change is detected, if a callback function is set, the asynchronous thread generates a state change event, puts the callback into the event queue, and executes it by the JS engine

How do the Browser process and the Browser kernel (Renderer process) communicate

Open a browser and you can see that there are two processes in the task manager (the main process and the renderer that opens the Tab page)

  • BrowserThe main process receives a user request, first needs to obtain the page content (such as downloading resources over the network), and then passes the taskRendererHostInterface pass toRenderRendering process
  • RenderRenderer proceduralRendererThe interface receives the message, briefly interprets it, and passes it to the renderer threadGUIThen start rendering
  • GUIThe render thread receives the request, loads the page and renders the page, which may be requiredBrowserThe main process gets resources and needsGPUProcess to help render
  • Of course there might beJSThread operationDOM(Note: This may cause backflow and redraw.)
  • The lastRenderThe renderer process passes the result toBrowserThe main process
  • BrowserThe main process receives the result and draws it

Renderer gets the page resources through RendererHost. Renderer renders the page. Renderer passes the rendered results back to Browser

Comb through the browser rendering process

The main process takes over, opens a download thread, makes an HTTP request (tcp3-way handshake) (leaving out DNS queries, IP addressing, etc.), and waits for a response. Get content -- pass the content to the browser Renderer process through the RendererHost interface and the browser rendering process beginsCopy the code

Once the browser kernel gets hold of the content, rendering can be broken down into the following steps:

  • Parse the HTML to create a DOM tree

  • Parse the CSS to build a CSS tree

  • Run the JS script, wait until the JS file is downloaded, operate DOM Tree and CSS Rule Tree through DOM API and CSS API, and then combine CSS Tree and DOM into render Tree.

  • Render tree (Layout /reflow), responsible for each element size, location calculation

  • Render the Render tree (paint), which draws the page pixel information

  • The browser will send the information of each layer to the GPU process, and the GPU will display the composite of each layer on the page. After the rendering is completed, the load event will be processed by its own JS logic

GUI rendering threads and JS engine threads are mutually exclusive, preventing page rendering.

Solution:

It is recommended to place the

The difference is that async executes immediately after downloading; Defer will wait until the DOM Tree parsing is complete.

The running mechanism of JS is discussed from Event Loop

First we need to know a few things:

1.JavaScript is a single-threaded language, because it was originally designed to handle web page interactions in a browser. The browser has only one thread (hence the single thread) responsible for interpreting and executing JavaScript, the JS engine thread, but the browser also provides other threads, such as event-triggered threads, timer-triggered threads, and so on.

2. Asynchronous generally refers to:

  • Network request
  • The timer
  • DOM event listening

3. Event cycle mechanism:

  • JavaScript is a single-threaded language

  • Event Loop is the execution mechanism of javascript

  • JS is divided into synchronous tasks and asynchronous tasks

  • The JS engine thread maintains an execution stack, and the synchronous code is successively added to the execution stack and successively executed.

  • When the JS engine thread encounters an asynchronous function, it will hand the asynchronous function to the corresponding Webapi and continue to perform the following tasks. Besides the main thread, the event-triggering thread manages a task queue, that is, an event queue.

  • Webapi queues the asynchronous callback to the message queue when the condition is met.

  • When the stack is empty, the JS engine thread will fetch the callback function (if any) from the event queue and add it to the stack for execution.

  • After the completion of the stack (at this time the JS engine idle), the stack is empty again, repeat the above operation, this is the event loop mechanism.

The picture above is roughly:

  • The execution stack is generated when the main thread runs,

  • When code in the stack calls some API, they add various events to the event queue (when triggering conditions are met, such as Ajax requests, Dom onClick, setTimeOut,setInterval)

  • When the code on the stack finishes executing, it reads the events in the event queue to execute those callbacks

  • The cycle

  • Note that events in the event queue are always read after the code in the stack completes

    Here’s an interview question:

    console.log('script start');
    setTimeout(function() {
        console.log('setTimeout1');
    }, 10);
    Promise.resolve().then(function() {
        console.log('promise1');
    }).then(function() {
        console.log('promise2');
    });
    setTimeout(function() {
        console.log('setTimeout2');
    }, 0);
    console.log('script end');
    Copy the code

    Print order:

    'script start'
    'script end'
    'promise1'
    'promise2'
    'setTimeout2'
    'setTimeout1'
    Copy the code

    Why is that? Because there’s 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 is called task. The difference between them? A simple point can be understood as follows:

  • Macrotask (also known as macrotask), which can be understood as a macrotask each time the code executes on the execution stack (including fetching event callback from the event queue and placing it on the execution stack each time)

    • Each task completes the task from start to finish
    • In order to ensure the orderly execution of internal JS tasks and DOM tasks, browsers will re-render the page after the completion of one task and before the start of the next task (Task - > - > GUI rendering task - >...)
  • A microtask (also known as a microtask) is a task that can be executed immediately after the execution of the current task

    • That is, after the current task, before the next task, and before rendering
    • So it responds faster than setTimeout (setTimeout is task) because there is no need to wait for rendering
    • That is, after a macroTask is executed, all microtasks generated during its execution will be executed (before rendering). What scenarios will form macroTasks and microTasks?

Highlight – Rules:

Macrotask: includes main block script, setTimeout, setInterval, requestAnimationFrame, etc. (as you can see, each event in the event queue is a MacroTask)

Microtask: promise. then catch finally, process.nextTick (node environment), etc

Note: In a Node environment, process.nextTick takes precedence over Promise, which means that the nextTickQueue portion of the microtask will be executed after the macro task ends, and then the Promise portion of the microtask will be executed.Copy the code

Let’s look at threads again:

  • Events in MacroTask are placed in an event queue, which is maintained by the event-triggering thread
  • All microtasks in microTasks are added to Job Queues, which wait for the current MacroTask to finish executing. This queue is maintained by the JS engine thread (because it is executed seamlessly under the main thread)
  • Summary of the operation mechanism:
    • Perform a macro task (fetch from event queue if not in stack)
    • If a microtask is encountered during execution, it is added to the task queue of the microtask (FIFO)
    • Execute all microtasks in the current microtask queue immediately after the macro task is executed (in sequence)
    • When the macro task completes, the render is checked, and the GUI thread takes over
    • After rendering, the JS thread takes over and starts the next macro task (fetched from the event queue)

practice

console.log('1')
setTimeout(function() {
    console.log('2')
    Promise.resolve().then(()=>{
    	console.log('3')
    })
    new Promise(function(resolve) {
        console.log('4')
        resolve()
    }).then(function() {
        console.log('5')
    })
},10)
Promise.resolve().then(()=>{
    	console.log('6')
    })
new Promise(function(resolve) {
    console.log('7')
    resolve('lc')
}).then(function(res) {
    console.log('8')
		return res
})

Copy the code

Print order:

1
7
6
8
Promise {<resolved>: "lc"}
2
4
3
5
Copy the code

Understanding of async await

A function with the async keyword that makes your function return a promise object

That is

  • If the async keyword function returns anything other than a promise, it is wrapped automatically in promise.resolve ()
  • If the async keyword function explicitly returns a promise, the promise you return takes effect
  • Resolve (undefiend) If the function has async keyword and no await keyword and no return value, it implicitly returns promise.resolve (undefiend)

Here is a simple example to see the difference between the return value of async keyword functions and normal functions:

Async function fn1(){return 123} function fn2(){return 123} console.log(fn1()) console.log(fn2())  Promise {<resolved>: 123} 123Copy the code

What is await waiting for?

Await is the result of the “expression” on the right

In other words,

If it is a function on the right, then the return value of the function is the result of the expression.

If it’s a ‘hello’ or something on the right, then the result of this expression is ‘hello’.

async function async1() { console.log('async1 start') await async2(); console.log('async1 end'); Console.log ('async22')} async function async2() {console.log('async2 ')} async1() console.log(' script start')  async1 start async2 script start async1 end async22Copy the code

Note that async2 after await will give up the thread and block the following code.

With await, there are two cases where it gives up the thread and blocks the following code

  • Not a promise object
  • Object is a promise

Await blocks code, promise or no promise, after async2 (async2 console.log(‘async1 end’)) is performed, Log (‘async1 end’); async1 (‘async1 end’);

Classic interview questions:

async function async1() {
    console.log("async1 start");
    await async2();
    console.log("async1 end");
}
async function async2() {
    console.log("async2");
}
console.log("script start");
setTimeout(function() {
    console.log("setTimeout");
}, 0);
async1();
new Promise(function(resolve) {
    console.log("promise1");
    resolve();
}).then(function() {
    console.log("promise2");
});
console.log("script end");

Copy the code
Print: script start async1 start async2 promise1 Script end async1 end promise2 setTimeoutCopy the code

Concepts of macro and micro tasks:

When a piece of code is executed, the synchronized code in the macro task is executed first,

  • If macro tasks such as setTimeout and setInterval are encountered during execution, the function inside setTimeout is pushed into the “macro task queue” and called when the next macro task is executed.
  • If a microtask such as promise.then() is encountered during execution, it will be pushed to the “microtask queue of the current macro task” and execute all the microtasks 1, 2, and 3 in sequence after the synchronous code execution of this round of macro tasks is completed
  • Async and await functions, if confronted with await keyword, will execute the result following await and then give up the thread (the current async function) and block the execution of the following code;

Analysis process:

Async1 and async2 are function declarations, so console. Log ("script start"), -- print: Script start 2.setTimeout as macro task 2 to wait in the event queue, 3. To execute async1(), print console.log("async1 start"), and before executing async2(), print console.log("async2"), Resolve (undefiend) and async1 is thrown. 4. New Promise is immediately executed as a constructor, console.log("promise1") is executed, and then() is put into the microtask queue of macro task 1; Script start->async1 start-> async2->promise1 5 Execute console.log for the main thread ("script end") -- print: script start->async1 start-> async2->promise1->script end 6. Enter async1(), execute console.log("async1 end"), execute macro task 1 microtask, console.log("promise2") -- print: script start->async1 start-->async2->promise1->script end->async1 end->promise2 7. Now macro task 1 and its microtasks are finished, the stack is free, the GUI thread renders, fetch macro task 2 (setTimeout lines) from the event queue, execute console.log("setTimeout") -- print: script start->async1 start-->async2->promise1->script end->async1 end->promise2->setTimeoutCopy the code
According to the optimized new specification, Function async1(){console.log('2 async1 start') return async2().then(() => {console.log('6 async1 end')) // Put into the microtask queue, } function async2(){console.log('3 async2') return promise. resolve(undefined)} console.log('1 script start') setTimeout(function(){ console.log('8 setTimeout') },0) async1() new Promise(function(resolve){ console.log('4 promise1') resolve(); }). Then (function(){console.log('7 promise2')}) console.log('5 script end' async2 4 promise1 5 script end 6 async1 end 7 promise2 8 setTimeoutCopy the code
Consolidation exercises:  async function async1() { console.log('2') await async2() console.log('6') } async function async2() { console.log('3')  } setTimeout(function () { console.log('8') }, 0) console.log('1') async1() new Promise(function (resolve) { console.log('4') resolve() }).then(function () { console.log('7') }) console.log('5') 1. The synchronization code 1 is printed first, and then the async1 method is printed 2. 2. As we encounter await, we enter async2 method first, and the following microtask, 6, is in wait state. 3. Print "3" in async2, now jump out of async and execute the outside synchronization code first. 4. Output 4, 5. The 7 in the then callback goes to the microtask stack. 6. Run macro task 2 (setTimeout), output 8Copy the code

Added a point:

  • RequestAnimationFrame (the execution time is determined by the screen display refresh rate, the higher the display screen refresh rate, the smoother the animation)

    • Concept: NEW HTML5 API, similar to setTimeout timer. One way to the window object window. RequestAnimationFrame browser (so can only be used in the browser) specially provides the API for animation, animated dom, canvas animation, SVG animation, webGL has a uniform refresh mechanism and so on animation.

    • Basic idea: Keep the frequency of page redrawing in sync with this refresh rate

      • For example, if the monitor screen refresh rate is 60Hz and the requestAnimationFrame API is used, then the callback function is executed every 1000ms / 60 ≈ 16.7ms; If the refresh rate of the display screen is 75Hz, the callback function will be executed every 1000ms / 75 ≈ 13.3ms. The higher the refresh rate of the display screen, the smoother the animation will be.
      • The interval between page redrawing or rewinding caused by calling the callback function through requestAnimationFrame is the same as the interval between display refresh. So requestAnimationFrame doesn’t need to pass the interval like setTimeout, but rather the browser picks up and uses the monitor refresh frequency from the system
    • Features:

      • Redraw a web page by frame. This method tells the browser animation to call a function to update the animation before the next redraw.
      • It is up to the system to determine the execution mechanism of the callback function. The browser optimizes method calls automatically at runtime. The monitor has a fixed refresh rate (60 hz or 75Hz), which means it can only be redrawn 60 or 75 times per second at most.
    • Advantage:

      • From the realization of functions and use methods, improve performance, prevent frame drop.
      • Browser UI threads: Browsers share a single thread for executing JavaScript and updating the user interface (including redrawing and reflow), called the “browser UI thread.”
      • The browser UI thread works based on a simple queue system, where tasks are stored until the process is idle. Once idle, the next task in the queue is extracted and run again. These tasks are either running JavaScript code or performing UI updates.
  • SetTimeout (by artificially setting an interval time to constantly change the image, to achieve the animation effect, will lose frames, slow **)

    • Concept: By setting an interval to constantly change the image, to achieve animation effect. This method will appear stutter and jitter on some low-end machines. There are generally two reasons for this phenomenon:
    • The execution time of setTimeout is not fixed.
    • RequestAnimationFrame Refresh rate depends on screen resolution and screen size. The refresh rate may vary depending on the device. SetTimeout can only be set to a fixed interval, which may vary with the screen refresh interval.
    • In both cases, the execution pace of setTimeout is inconsistent with the refresh pace of the screen, resulting in frame loss. The biggest advantage of using requestAnimationFrame for animation is that it ensures that the callback function is executed only once in every screen refresh interval, so that frames are not lost and the animation is not stuck. B. Save resources and power

Conclusion:

SetTimeout differs from requestAnimationFrame:

  • Engine level: setTimeout belongs to JS engine, event polling exists, and event queue exists. RequestAnimationFrame belongs to the GUI engine and occurs during the redraw and rearrangement part of the rendering process, consistent with computer routing.

  • Performance level: When the page is hidden or minimized, timer setTimeout still performs animation tasks in the background. When a page is inactive, the screen refresh task for that page is paused by the system and requestAnimationFrame is stopped.

  • Application level: Use setTimeout, the timing mechanism to animate and simulate the page refresh at a fixed time. RequestAnimationFrame is an API provided by the browser specifically for animation. The browser automatically optimizes method calls at runtime, which can save CPU overhead in specific environments.

  • ** Redraw and reflux (rearrangement)**

    • Return:

      Backflow is when layout or geometry properties need to be changed. Backflow is a key factor affecting browser performance because it involves updating the layout of part of the page (or the entire page). The backflow of an element can result in subsequent backflow of all its child elements, as well as subsequent nodes in the DOM and ancestor node elements.

    • Redraw:

      Redraw occurs when the geometry of a node changes or when the style changes without affecting the layout. For example, outline, visibility, color, background-color, etc., can be expensive to redraw because the browser must verify the visibility of other node elements in the DOM tree.

    • When does backflow happen?

      • 1. Add or remove visible DOM elements

      • 2. The position of the element changes

      • 3. The size of the element changes (including margin, inner border, border size, height, width, etc.)

      • 4. Content changes, such as text changes or an image being replaced by another image of a different size.

      • 5. When the page is first rendered (this is inevitable)

      • 6. Browser window size changes (because backflow calculates element position and size based on viewport size)

      Redraw refers to the situation where the layout and geometry remain the same, such as changing background-color, or changing the font color, etc.

      Note: Backflow must trigger redraw, and redraw does not necessarily backflow

    • How to reduce backflow and redraw

      1, CSS optimization method

      • (1) Replace top with transform

        • (2) Replace display: None with undefined, because the former will only cause redraw and the latter will cause backflow

        • (3) Avoid the use of table layout, a small change may cause the entire table layout.

        • (4) Change the class as far as possible at the end of the DOM tree. Backflow is inevitable, but its impact can be reduced. Changing the class as far down the DOM tree as possible limits the scope of backflow, affecting as few nodes as possible.

        • (5) Avoid setting multi-layer inline style, CSS selector from right to left to match the search, avoid too many node levels.

        • (6) Apply the animation effect to the element with position attribute absolute or fixed to avoid affecting the layout of other elements, so that only a redraw, not backflow, and control the animation speed can select requestAnimationFrame. See requestAnimationFrame for details.

        • (7) Avoid using CSS expressions, which may cause backflow.

        • (8) Set the node that is frequently redrawn or reflow as a layer. The layer can prevent the rendering behavior of this node from affecting other nodes, such as will-change, video, iframe, etc., and the browser will automatically turn this node into a layer.

        • (9) CSS3 hardware acceleration (GPU acceleration). Using CSS3 hardware acceleration, transform, opacity, filters and other animations will not cause backflow redrawing. Other properties of animations, such as background-color, will still cause backflow redraw, but it can still improve the performance of those animations.

        2. JavaScript optimization method

        • (1) Avoid frequent manipulation of styles. It is better to overwrite the style property at once, or define the style list as class and change the class property at once.

        • (2) to avoid the frequent operation DOM, create a documentFragment, apply all DOM operation on it, and then add it to the Document, the Document. The createDocumentFragment () create a Document fragments, does not result in a back page.

        • (3) Avoid frequently reading properties that cause backflow/redraw, and if you do need to use them more than once, cache them in a variable.

Reference: Understanding async functions

Nguyen other es6

The 8 diagrams show you step by step the execution order of async/await and promise

Browser processes, JS event loops, macro tasks and microtasks

Document. CreateDocumentFragment () to create a Document fragments