preface

Whether it’s a job or an interview, we’ll often encounter situations where we need to know the order in which code is executed, so we’re going to spend some time thoroughly understanding how JavaScript works.

If this article has helped you, please follow ❤️ + like ❤️ and encourage the author

To understand the JavaScript execution mechanism, you need to understand the following :(in a browser environment, as opposed to a Node environment, for example)

  • The concept of processes and threads
  • Principles of browsers
  • Event-loop, task queue (synchronous task, asynchronous task, micro task, macro task)

Processes and threads

You probably learned about processes and threads in your college operating system principles class. Let’s take a look back

We all know that the core of a computer is the CPU, which undertakes all the computing tasks; The operating system is the manager of the computer, it is responsible for task scheduling, resource allocation and management, command the entire computer hardware; An application is a program that has some function and runs on the operating system.

process

Process is a dynamic execution process of a program with independent functions in a data set. It is an independent unit of the operating system for resource allocation and scheduling. It is the carrier of application program operation.

Characteristics of a process:

  • Dynamic: process is an execution process of the program, which is temporary and has a life span, dynamic generation and dynamic extinction.
  • Concurrency: Any process can execute concurrently with other processes.
  • Independence: a process is an independent unit of the system for resource allocation and scheduling.
  • Structure: process consists of program, data and process control block.

thread

Thread is a single sequence control flow in program execution. It is the smallest unit of program execution flow and the basic unit of processor scheduling and dispatching. A process can have one or more threads that share the memory space of the program (that is, the memory space of the process in which it is running). A standard thread consists of a thread ID, the current instruction pointer (PC), registers, and a stack. A process consists of memory space (code, data, process space, open files) and one or more threads.

The difference between processes and threads

  • Thread is the smallest unit of program execution, and process is the smallest unit of resources allocated by the operating system;
  • A process consists of one or more threads, threads are different lines of code execution in a process;
  • Processes are independent from each other, but each thread under the same process shares the program’s memory space (including code segments, data sets, heap, etc.) and some process-level resources (such as open files and signals), and is invisible to each other.
  • Scheduling and switching: Thread context switching is much faster than process context switching.

Why is JS single threaded?

JavaScript has been a scripting language for browsers since its inception, primarily to handle user interactions and manipulate the DOM, which means that it has to be single-threaded, or it can cause very complex synchronization issues.

For example 🌰 : if JS is multi-threaded and one thread wants to modify a DOM element and another thread wants to delete it, the browser doesn’t know who to listen to. So to avoid complexity, JavaScript was designed from the beginning to be single-threaded.

In order to make use of the computing power of multi-core CPU, 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 cannot operate DOM. So, this new standard doesn’t change the single-threaded nature of JavaScript

Principles of browsers

As a front-end engineer, browsers are no stranger, and browsers are multi-process.

Browser components

  • User interface: including address bar, forward/back/refresh/bookmark 🔖 and other buttons
  • Browser engine: Transmits instructions between the user interface and the rendering engine
  • Rendering engine: Used to draw the requested content
  • Network: Used to complete network calls, such as HTTP requests, with platform-independent interfaces that work on different platforms
  • JavaScript interpreter: Used to parse executing JavaScript code
  • User interface back end: Used to draw basic widgets, such as combo boxes and Windows, using the operating system’s user interface underneath
  • Data storage: Belongs to the persistence layer, the browser saves all kinds of data like cookies in the hard disk, HTML5 defines the Web Database technology, which is a lightweight complete client storage technology

⚠️ Note: Unlike most browsers, Google (Chrome) has a rendering engine instance for each TAB page. Each TAB is a separate process

Which processes are included in the browser

Browser process

  • The browser’s main process (responsible for coordination and control), which has only one process
  • Responsible for browser interface display and user interaction. Forward, backward, etc
  • Responsible for page management, creating and destroying other processes
  • Draw a Bitmap in memory from the Renderer process onto the user interface
  • Network resource management, download, etc

Third-party plug-in processes

  • Manage third-party plug-ins

GPU process

  • Responsible for 3D drawing and hardware acceleration (maximum one)

Rendering process

  • Responsible for page document parsing, execution and rendering

Which threads are included in the renderer process

GUI rendering thread

  • Mainly responsible for parsing HTML, CSS, building DOM tree 🌲, layout, drawing, etc
  • This thread is mutually exclusive with the JavaScript engine thread, and when the JavaScript engine thread executes, the GUI rendering thread is suspended, and when the task queue is idle, the main thread performs GUI rendering

JavaScript engine threads

  • Mainly responsible for handling JavaScript scripts and executing code (e.g. V8 engine)
  • A browser can only have one JS engine thread running a JS program at a time, i.e. JS is single threaded
  • The JS engine thread and the GUI rendering thread are mutually exclusive, so the JS engine blocks the page rendering

Timing trigger thread

  • Responsible for executing timer functions (setTimeout, setInterval)
  • Browser timing counters are not counted by the JS engine (since JS is single-threaded, blocking would affect the counter’s accuracy)
  • By a separate thread to time and trigger timing (after the timing is finished, added to the event trigger thread event queue, waiting for the JS engine idle after execution), this thread is the timing trigger thread, also known as the timer thread
  • W3C specifies requirements in the HTML standardsetTimeoutIf the interval is less than 4ms, it is 4ms

Event trigger thread

  • Responsible for handing the prepared event to the JS engine thread for execution
  • When an event is triggered, the thread adds the event to the end of the queue, waiting for the JS engine to process it

Asynchronous request thread

  • The browser opens a thread after an XMLHttpRequest connection
  • When detecting a request state change, if there is a corresponding callback function, the asynchronous request thread will generate a state change event, and put the corresponding callback function into the queue for the JS engine to execute

Synchronous and asynchronous

Because JavaScript is single-threaded, its tasks cannot only be synchronous tasks. Those tasks that take a long time will cause page blocking if they are executed as synchronous tasks. Therefore, JavaScript tasks are generally divided into two categories:

Synchronization task

A synchronization task refers to a task that is queued to be executed on the main thread. The next task can be executed only after the first task is completed.

Asynchronous tasks

Asynchronous tasks are tasks that do not enter the main thread but enter the Event queue. The task queue notifies the main thread that an asynchronous task is ready to execute.

Common asynchronous tasks: timers, Ajax, event bindings, callback functions, Promises, async await, etc

  • Synchronous and asynchronous tasks go to different execution “places”, synchronous tasks go to the main thread, asynchronous tasks go to the Event Table and register functions.
  • This function is moved to the Event Queue when the Event specified in the Event Table completes.
  • If the tasks in the main thread are empty after execution, the Event Queue will read the corresponding function and enter the main thread for execution.
  • This process is repeated over and over again, known as an Event Loop.
  • How do we know that the main thread stack is empty? The js engine has a monitoring process that continuously checks to see if the main thread stack is empty, and if it is, checks the Event Queue to see if there are any functions waiting to be called.

Macro and micro tasks

In addition to the broad definition of synchronous and asynchronous tasks in JavaScript, there are more detailed task definitions:

Macro task: includes global code, setTimeout, setInterval

Micro-task: New Promise().then(callback) process.nexttick ()

Different types of tasks enter different task queues:

The order of the event loop determines the execution order of the JS code. After entering the overall code (macro task), the first loop begins. Then perform all the microtasks. Then start from the macro task again, find one of the task queues to complete, and then execute all the microtasks.

Execution stack and task queue

Execution stack

JavaScript code is executed in an execution context, and there are three execution contexts in JavaScript:

  • Global execution context
  • Function execution context, a function execution context is created whenever a JS function is called
  • Eval Execution context, the context generated by the eval function (used less frequently)

Generally speaking, our JS code has more than one context, so what is the order of execution of these contexts?

As we all know, the stack is a lifO data structure. The execution stack in JavaScript is such a stack structure. When the JS engine executes the code, it will generate a global context and push it into the execution stack. The engine executes the function from the top of the stack and pops up the execution context when it is finished.

function add(){
  console.log(1)
  foo()
  console.log(3)}function foo(){
  console.log(2)
}
add()
Copy the code

Let’s take a look at the stack of this code:

Task queue

As we mentioned earlier, all tasks in JavaScript are divided into synchronous tasks and asynchronous tasks. Synchronous tasks, as the name implies, are tasks that are executed immediately, which are usually executed directly into the main thread. Our asynchronous task enters the task queue and waits for the main task to complete.

A task queue is a queue of events that indicate that related asynchronous tasks can be placed on the execution stack. The main thread reads the task queue to read what events are in it.

A queue is a first-in, first-out data structure.

As mentioned above, asynchronous tasks can be divided into macro tasks and micro tasks, so task queues can also be divided into macro task queues and micro task queues

  • Macrotask Queue: Perform large tasks, such as setTimeout, setInterval, user interaction, UI rendering, etc.

  • Microtask Queue: perform smaller tasks, such as Promise, process. nextTick;

Event Loop ♻️ (event-loop)

  1. Synchronous tasks are placed directly into the main thread for execution, while asynchronous tasks (click events, timers, Ajax, etc.) hang in the background for execution, waiting for I/O events to complete or action events to be triggered.
  2. The system executes asynchronous tasks in the background. If an asynchronous task event (or behavior event) is triggered, the task is added to the task queue, and each task is processed by a callback function.
  3. The asynchronous task is divided into macro task and micro task. The macro task enters the macro task queue, and the micro task enters the micro task queue.
  4. The tasks in the execution task queue are specifically completed in the execution stack. When all the tasks in the main thread are executed, the microtask queue is read. If there are any microtasks, they are all executed, and then the macro task queue is read
  5. The above process is repeated over and over again, which is often referred to as an event-loop.

Sample validation

So let’s test that with a problem

(async() = > {console.log(1) 
  
    setTimeout(() = > {
    console.log('setTimeout1')},0);
  
    function foo (){
        return new Promise((res,rej) = > {
            console.log(2)
            res(3)})}new Promise((resolve,reject) = >{
    console.log(4)
    resolve() 
    console.log(5)
    }).then(() = > {
    console.log('6')})const res = await foo();
    console.log(res);
    console.log('7')
  
    setTimeout(_= > console.log('setTimeout2'))
})()
Copy the code

Print order is: 1,4,5,2,6,3,7 setTimeout1, setTimeout2

Analysis:

  • Code executes from top to bottom, encountered firstconsole.log(1), print directly1Then, the timer belongs to the macro task and is put into the macro task queue
  • Meet promise again, becausenew PromiseIs a synchronization task, so print directly4Resolve, the then function, is placed in the microtask queue, and printed5
  • And then executeawait fooFoo has a function in itpromise.new promiseIt is a synchronization task, so it will print directly2, await returns a promise callback, and the task behind await is put into the microtask queue
  • Finally, a timer is encountered and placed in the macro task queue
  • After the execution stack task is executed, the first microtask is obtained from the microtask queue, and the first microtask is executed and printed6, and then perform the second microtask, printing3, 7
  • After the micro task is executed, go to the macro task queue to obtain the macro task execution and printsetTimeout1,setTimeout2

Fun timer

Asynchronous tasks in JavaScript’s task queue also include timer events, which specify how long some code should be executed. Timer functions are mainly performed by setTimeout() and setInterval(). Their internal execution mechanisms are exactly the same. The difference lies in that setTimeout is a process of one-time execution, while setInterval is a process of repeated execution.

The setTimeout function takes two arguments, the first is the callback to execute and the second is the time to delay execution (ms).

If we set the delay to 0, will it be executed immediately?

setTimeout(() = >{
    console.log(1)},0)

console.log(2)
Copy the code

But that’s not the case. It prints 2 first, then 1. Do you feel confused?

We use the rules of the above event loop to understand is very clear, global code execution, setTimeout timer, into the macro task queue, and then to perform the synchronization code, to print 2, stack tasks performed to task queue, no small task to see macro task queue, there is a macro task, print 1.

SetTimeout (fn,0) means to specify that a task should be executed at the earliest available free time on the main thread, that is, as early as possible. It adds an event to the end of the “task queue”, so it does not execute until both the synchronized task and the existing events in the “task queue” are processed.

The HTML5 standard specifies that the second parameter of setTimeout() must be at least 4 milliseconds, and if it is below this value, it is automatically increased. Previously, older browsers had set the minimum interval to 10 milliseconds. In addition, DOM changes, especially those that involve page re-rendering, are usually not performed immediately, but every 16 milliseconds. RequestAnimationFrame () works better than setTimeout().

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

Recommended reading

How do you choose an array traversal from a performance perspective? This is called call,apply,bind. This is called call,apply,bind

Think the article is good, you can click a like ah ^_^ plus welcome to pay attention to the comment exchange ~