Basics of browser event loops

Processes and threads

  • A process is an independent resource allocated by the system. It is the basic unit of CPU resource allocation. A process is composed of one or more threads.
  • Thread is the execution flow of the process, which is the basic unit of CPU scheduling and dispatching. Multiple threads in the same process share the resources of the process.

The way in which processes communicate

Refer to the answer

1. Pipe: half-duplex communication mode, data can only flow one-way, can only be used between parent and child processes.

2. Named pipe FIFO: half-duplex communication, but it allows communication between unrelated processes.

MessageQueue: a MessageQueue is a linked list of messages stored in the kernel and identified by MessageQueue identifiers. The message queue overcomes the disadvantages of little signal transmission, pipe carrying only plain byte stream and limited buffer size.

4. SharedMemory: SharedMemory maps a segment of memory that can be accessed by other processes. This SharedMemory is created by one process but can be accessed by multiple processes. Shared memory is the fastest IPC method and is specifically designed for the low efficiency of other interprocess communication methods. It is often used in conjunction with other communication mechanisms, such as semaphores, to achieve synchronization and communication between processes.

A Semaphore is a counter that can be used to control access to a shared resource by multiple processes. It is often used as a locking mechanism to prevent other processes from accessing a shared resource while one process is accessing it. Therefore, it is mainly used as a means of synchronization between processes and between different threads within the same process.

6. Socket: Socket is also an interprocess communication mechanism. Different from other communication mechanisms, it can be used for communication between different processes.

Sinal: Signals are a complex form of communication used to inform the receiving process that an event has occurred.

Multi-process architecture for browsers

The main processes and responsibilities of the browser (see figure) :

  • Browser Process is the main Process

    • Responsible for the display and interaction of the browser interface. Manage each page, create and destroy other processes. Network resource management, download, etc.
  • Third-party plug-in Process Plugin Process

    • Each type of plug-in corresponds to a process that is created only when the plug-in is used.
  • GPU Process GPU Process

    • Only one at most, for 3D drawing etc
  • Renderer Process

    • Called the browser renderer process or browser kernel, it is multithreaded internally. Mainly responsible for page rendering, script execution, event processing, etc. (Key analysis in this paper)

Browser kernel

Browsers are multi-process, and the browser kernel (the browser renderer) belongs to one of the multi-process browsers. The browser kernel has multiple threads working:

  1. GUI rendering thread:
    • It renders the page, parses the HTML, constructs the DOM tree with CSS, and calls it when the page is redrawn or redrawn.
    • It is mutually exclusive with the JS engine thread. When the JS engine thread is working, the GUI rendering thread will be suspended, and the GUI update will be placed in the JS task queue, waiting for the JS engine thread to be idle.
  2. JS engine thread:
    • Single-threaded work, responsible for parsing and running javascript scripts.
    • It is mutually exclusive with the GUI rendering thread, and taking too long to run JS will cause the page to block.
  3. Event trigger thread:
    • When an event that meets the trigger condition is triggered, the thread will add the corresponding event callback function to the end of the task queue and wait for the JS engine to process it.
  4. Timer triggers thread:
    • Browser timing counters are not counted by the JS engine, and blocking can result in inaccurate timing.
    • Start the timer trigger thread to time and trigger the time, the time will be added to the task queue, waiting for the JS engine to process.
  5. HTTP request thread:
    • A request thread is opened when an HTTP request is made.
    • After the request completes, the callback function of the request is added to the task queue, waiting for the JS engine to process it.

JS event loop

  • Why is that?
    • JavaScript engines are single-threaded, which means that only one task can be executed at a time. Other tasks are queued up to be executed, and the next task can only be executed once the current one is complete. If a particular execution takes too long, it tends to block; To solve this problem, JavaScript introduces an event loop.
  • What is?
    • Tasks in a JavaScript single thread are divided into synchronous tasks and asynchronous tasks. As you can see in the following flow chart, the main thread continuously reads events from the task queue. This process is called an Event Loop.

JS event loop mechanism

The JavaScript event loop mechanism is divided into browser and Node event loop mechanism, and the browser part is mainly discussed here. Javascript has a main thread and a call-stack. All tasks are put on the call stack to wait for the main thread to execute. Here to understand the JS execution environment and scope, you can see this: Talk about the JS execution environment and scope.

  1. JS call stack
    • The JS call stack is a lifO data structure. When a function is called, it is added to the top of the stack and removed from the top of the stack until the stack is empty.
  2. Synchronous and asynchronous tasks
    • Tasks in a JavaScript single thread are divided into synchronous tasks and asynchronous tasks. Synchronous tasks queue up in order in the call stack to wait for the main thread to execute, while asynchronous tasks add the registered callback function to the task queue (message queue) to wait for the main thread to be idle, that is, when the stack is empty, to wait for the main thread to execute. A task queue is a first-in, first-out data structure.
  3. Event loop
    • The synchronous tasks in the call stack are all executed and the stack is emptied, indicating that the main thread is free. At this time, a task will be read from the task queue in order and put into the stack for execution. Each time the stack is cleared, the task queue will be read. If there is no task, the task queue will be read and executed. The read-execute operation will be repeated all the time, forming an event loop.

Macro and micro tasks under the event loop

We usually divide asynchronous tasks into macro tasks and micro tasks. They are distinguished by:

  • Macro-task: generally, it is a callback task generated by the communication between the JS engine and the host environment, such as setTimeout. SetInterval is timed by the browser. The execution time of the callback function needs to be notified to the JS engine by the browser, the network module, and the communication callback processed by I/O. The overall script code, setTimeout, setInterval, setImmediate, DOM event call-back, and the callback after the Ajax request ends are all. SetTimeout >setInterval>setImmediate
  • Micro-task: A callback generated when a macro task is executed in a thread. NextTick, Promise, Object.observe(deprecated), and MutationObserver (DOM listener) are all call-back that the JS engine itself can listen to. NextTick >Promise> object.observe

In the event loop, the task is usually performed by the macro task start (JS code to load the execution), in the macro task execution process, may produce new macro and micro tasks, the micro tasks will be added to the current task queue, the current macro finish task execution, will perform a task in the queue task, the current queue, micro task has been completed Then execute the new macro task.

Detailed analysis of typical cases

  • Case Note 1
setImmediate(function(){
  console.log(1);
},0);
setTimeout(function(){
  console.log(2);
},0);
new Promise(function(resolve){
  console.log(3);
  resolve();
  console.log(4);
}).then(function(){
  console.log(5);
});
console.log(6);
process.nextTick(function(){
  console.log(7);
});
console.log(8);
Copy the code

According to the running principle of JS to explain the above code execution order:

The code is executed from the script main program; Create setImmediate Macro task; Create a setTimeout macro task. If the macro task (script main program) has not finished executing, then create a callback to the Promise. Log (6) to print 6; Create microtask process.nextTick; Log (8) to print 8; Execute the micro tasks in the task queue, and output 7 and 5 in order of priority: process.nexttick > promise. then; SetTimeout > setImmediate Mediate 2,1/3 4, 6, 8, 7, 5, 2,1Copy the code
  • Case Note 2
setTimeout(function(){     
  console.log(1);   
},0)   
async function async1(){    
  console.log(2);    
  await async2();    
  console.log(3);   
}   
async function async2(){    
  console.log(4);   
}   
async1();   
new Promise(function(resolve,reject){    
  console.log(5);    
  resolve();   
}).then(function(e2){    
  console.log(6);   
})   
console.log(7);
Copy the code

According to the running principle of JS to explain the above code execution order:

The code is executed from the script main program; Create a setTimeout macro task. After async1() is executed, wait for the return of await value (2,4 is printed at this time) to continue to execute the main program code, and the code after await is blocked and added to the task queue for execution; Perform Promise output 5. Create a callback to the microtask promise.then before the macro task (script main program) has finished executing. Log console.log(7) to print 7; Execute the microtasks in the task queue and print the code output after await (3) and the callback (6) of promise.then in order. After the microtask is complete, run the macro task setTimeout and print 1. // 3Copy the code

Note that the statement to the right of “await” in async is executed immediately, and the code below waits until “await” returns the value of “await” before proceeding to the next statement.

  • Case 3
const pro = new Promise((resolve, reject) = > {
  const innerpro = new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve(1);
    }, 0);
    console.log(2);
    resolve(3);
  });

  innerpro.then(res= > console.log(res));  // res = 3 To join the task queue

  resolve(4);
  console.log("pro");
})
pro.then(res= > console.log(res));  // res = 4 Joins the task queue

console.log("end");

/ / 2, pro, end, 3, 4
// A promise can only be resolved once, so 1 will not be printed
Copy the code
  • Four cases
const pro = new Promise((resolve, reject) = > {
  console.log('1');
  resolve();
}).then(() = >{
  console.log('2');
  new Promise((resolve, reject) = > {
    console.log('3');
    resolve();
  }).then(() = >{
    console.log('4');
  }).then(() = >{
    console.log('5');
  })
}).then(() = >{
  console.log('6');
})
// 1 2 3 4 6 5
Copy the code

References: References 1, 2, 3