JavaScript: event loop mechanism (EventLoop)

1. Understand processes and threads

A process is one of the basic concepts in the operating system. It refers to a running program, including the program's execution code, data, resources, etc. The operating system allocates certain system resources to each process, such as memory space, files, and devices, so that the process can run normally.

A thread is an execution flow in a process and can be regarded as an independent execution unit in the process. Each process can contain multiple threads, and these threads share the process's resources. Unlike processes, threads cannot exist independently and must depend on processes. Threads perform tasks by utilizing the resources of the process and can be switched within the process.

Simply put, a process is the smallest unit of resource allocation and management, and a thread is the smallest unit of program execution. In a multitasking environment, multiple processes can be executed at the same time, and multiple threads in the same process can also be executed at the same time. Thread switching is less expensive than process switching, so multi-threaded programs are more efficient.

2. Understand single-threading and multi-threading

Single-threading means that the program has only one execution thread and can only execute tasks serially, that is, each task must wait for the previous task to be completed before proceeding to the next task. The advantages of this model are simple logic and low development and maintenance costs, but the execution efficiency is low, especially when processing large amounts of data, it is prone to problems such as blocking.

Multi-threading means that a program has multiple execution threads at the same time. Each thread can run independently and handle multiple tasks at the same time. Multi-threading can greatly improve the execution efficiency and responsiveness of a program, which is especially useful when multiple tasks or large amounts of data need to be processed at the same time. However, the disadvantages of multi-threading are high complexity, prone to concurrency problems, and the need to consume more system resources and development costs.

Single threading is suitable for simple applications, while multi-threading is suitable for complex applications, which can greatly improve the execution efficiency and responsiveness of the program.

3. Understand the rendering thread

A rendering thread refers to a computer process thread used to render graphics. In a web browser, the rendering thread is responsible for converting HTML, CSS, and JavaScript code into visual content on the web page. The main task of the rendering thread is to parse the HTML document, determine the rendering tree and draw the image. It is also responsible for inspecting and processing JavaScript code for interactive operations on web pages.

The role of the rendering thread is to display the content requested by the user as quickly as possible without affecting the user interface. It takes care to avoid blocking the user interface thread, so it needs to run in the background and minimize CPU and memory usage.

In some cases, the rendering thread may need to wait for a network request or file loading to complete, which may cause the page to freeze or load slowly. To reduce the occurrence of this situation, the rendering thread will often use caching technology to display the web page quickly when the same content is loaded.

4. Understand microtasks and macrotasks

Microtasks and macrotasks are both task types in the JavaScript event loop mechanism. The difference between them is the timing and priority of execution.

Macro tasks include some time-consuming tasks, such as setTimeout, setInterval, AJAX requests, DOM events, etc. After these tasks are executed, you need to wait for the next event loop to start before executing the next task. When executing a macro task, the first task in the queue to enter the queue will be selected for execution until the queue is empty.

Microtasks refer to tasks that are relatively short and need to be executed immediately, such as Promise's then method, MutationObserver, etc. When these tasks are executed, they will be executed immediately after the current macro task is completed. When microtasks are generated in a macrotask, these microtasks will be placed in a special queue and will be executed after the current macrotask is completed. When executing these microtasks, if new microtasks are generated, these new microtasks will be placed at the end of the queue, waiting for execution.

At the beginning of the event loop, all tasks in the microtask queue are executed until the queue is empty. Then execute the tasks in the macro task queue. Then execute the tasks in the microtask queue until the queue is empty. This process will be repeated until the task queue and the microtask queue are both empty.

In summary, microtasks have a higher priority and will be executed before macrotasks. Therefore, when writing asynchronous code, you should pay attention to using appropriate task types to ensure the correctness and efficiency of the code.

6. Understand synchronous tasks and asynchronous tasks

Synchronous tasks mean that the code is executed in sequence, and each task is completed before proceeding to the next task until all tasks are completed. In a synchronous task, code execution needs to wait for the previous code execution to complete before continuing with the next task.

Asynchronous tasks mean that during the execution of the code, you do not have to wait for the previous task to complete before executing the next task. Asynchronous tasks can be executed on a single thread, but multiple tasks can be performed simultaneously. Asynchronous tasks usually use callback functions or Promises to implement asynchronous processing.

For example: Suppose there is a task list, including tasks such as reading files, sending data, waiting for responses, etc. If you use synchronous task execution, the task of reading the file must not start until the task of sending the data is completed. In an asynchronous task, the file reading task can be performed while sending data, so the task can be completed more efficiently.

7. Understand the call stack

The call stack is a memory structure used to track the sequence of program execution. When functions are called, their related information (such as local variables, return addresses, etc.) will be pushed into the call stack, which operates according to the "first in, last out" principle. That is, when functions return, their related information will be popped from the stack so that program execution can continue. Through the call stack, we can trace the execution process of the program and see the order in which each function is called and the relationship between them. The call stack is also one of the important tools when debugging a program, which can help programmers pinpoint problems in the program.

8. JavaScript was designed to be single-threaded

1. Simplicity: Single-threaded code is easy to write, debug and maintain, and is not prone to multi-thread competition issues.

2. Security: Multi-threads need to share memory, which can easily cause problems such as data competition. JavaScript, as a scripting language, usually runs in a browser environment, and there are many threats from malicious scripts. If JavaScript is multi-threaded, malicious scripts may modify the data of other scripts through shared memory, causing security issues.

3. Predictability: A single thread can ensure that the execution order of events is predictable, thus avoiding some complex concurrency scenarios.

Of course, JavaScript also provides some very important asynchronous APIs, such as setTimeout, setInterval, Promise, async/await, etc. These APIs can simulate the effect of multi-threading, but they are still single-threaded in nature.

9. Why JavaScript needs asynchronous

1. Prevent blocking: JavaScript is a single-threaded language. If all tasks are executed synchronously, when a time-consuming operation (such as a network request or file reading and writing) is performed, the entire application will be blocked, resulting in a poor user experience. .

2. Improve user experience: Asynchronous programming allows JavaScript to continue to respond to user operations while performing time-consuming operations, thereby improving user experience.

3. Save resources: Asynchronous programming can make better use of computer resources and improve execution efficiency by executing multiple tasks in parallel.

4. Support cross-platform development: JavaScript is widely used on different platforms such as web, mobile and back-end. The use of asynchronous programming mode can support a variety of asynchronous events, making the code more portable.

10. What are the asynchronous operations in JavaScript?

serial number operate
1 Callback
2 Promise
3 async/await
4 event listening
5 timer
6 Network requests such as XMLHttpRequest and Fetch API
7 Web Workers (Web Worker API provides the ability to separate from the main execution thread and run scripts in the background, that is, running JavaScript code in the background without affecting the rendering and responsiveness of the page UI)
8 Asynchronous I/O operations in Node.js (such as reading and writing files, network requests, etc.)

11. How does JavaScript achieve asynchronous single-threading? Understand the JavaScript event loop mechanism

With the previous foundation, let’s discuss the core event loop of this article.

JavaScript single thread means that only one task can be executed at the same time, and the task can only start execution after the previous task is completed. But JavaScript achieves asynchrony through the event loop mechanism.

The JavaScript event loop mechanism refers to the mechanism by which the JavaScript runtime environment (defined by the ECMAScript specification) handles asynchronous operations and events in the code according to certain rules. The JavaScript event loop mechanism includes task queue (Task), microtask queue (Microtask) and macro task queue (Macrotask).

When JavaScript code executes an asynchronous operation or event, it does not execute it immediately, but puts it into the corresponding task queue. When the current task is completed, at the beginning of the next event loop, JavaScript will take out a task from the task queue and execute the task. When a task is executed, new asynchronous operations and events may be generated, and these new operations will also be placed in the task queue waiting for execution.

In the JavaScript event loop mechanism, tasks are divided into macro tasks and micro tasks.
Macro tasks include setTimeout, setInterval, I/O operations, etc.;
Micro tasks include Promise, MutationObserver, process.nextTick, etc.
At the beginning of each event loop, JavaScript will first execute all the tasks in the microtask queue, and then execute the tasks in the macrotask queue.

A complete event loop process includes the following steps:

1. Take out a task from the macro task queue for execution;
2. If microtasks are generated in the task, put them into the microtask queue;
3. Execute all tasks in the microtask queue;
4. Check whether the page needs to be re-rendered;
5. Repeat the above steps, Until there are no tasks in the task queue and microtask queue;

The event loop mechanism refers to:

1. When the code is executed, the synchronous task is executed first, and then the asynchronous task is placed in the task queue, waiting for execution.
2. When all synchronization tasks are completed, the JavaScript engine will read the tasks in the task queue.
3. Push the first task in the queue into the execution stack for execution, and pop it out of the stack after execution.
4. Repeat this loop until all tasks in the task queue are executed.

This is the basic principle of asynchronous implementation in JavaScript, by placing asynchronous tasks in the task queue and implementing asynchronous execution through the event loop mechanism.

In general, JavaScript implements asynchronous operations through the event loop mechanism. The asynchronous tasks are placed in the task queue, and then wait for execution in the task queue until the JavaScript engine is idle, and then take out the tasks in the task queue for execution.

12. Exercises

1. Which of the following operations are asynchronous?

下面哪些操作是异步的?

a. 将数组元素进行排序
b. 发送请求获取数据
c. 计算两个数的和
d. 读取本地文件

答案:b和d

2. What does the following code output?

console.log('start')

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

console.log('end')

答案:start, end, timeout

解析:setTimeout函数中的第二个参数表示延迟的时间,当设置为0时,setTimeout函数会被放到任务队列的末尾,等待执行栈中所有任务执行完成后再执行setTimeout函数中的回调函数。

3. What does the following code output?

console.log('start')

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

new Promise((resolve) => {
  console.log('promise1')
  resolve()
}).then(() => {
  console.log('then1')
})

console.log('end')

答案:start, promise1, end, then1, timeout1

解析:Promise对象是同步执行的,所以会先输出promise1。但是.then()方法是异步执行的,会被放到任务队列中等待执行,因此end会先输出。然后在任务队列中执行.then()方法,输出then1。最后在任务队列中执行setTimeout中的回调函数,输出timeout1。

4. What does the following code output?

console.log('start')

setTimeout(() => {
  console.log('timeout1')
  Promise.resolve().then(() => console.log('then2'))
}, 0)

new Promise((resolve) => {
  console.log('promise1')
  resolve()
}).then(() => {
  console.log('then1')
  setTimeout(() => {
    console.log('timeout2')
  }, 0)
})

console.log('end')

答案:start, promise1, end, then1, timeout1, then2, timeout2

解析:同样的,Promise对象和.then()方法是同步执行的,但是回调函数中包含的Promise对象和.then()方法时异步执行的,会被放到任务队列中等待执行。因此start, promise1, end, then1会先输出。然后在任务队列中执行setTimeout中的回调函数,输出timeout1,然后将包含的Promise对象和.then()方法放到任务队列中。在任务队列中执行.then()方法,输出then2。最后在任务队列中执行第二个setTimeout中的回调函数,输出timeout2。

5. What does the following code output?

console.log('start'); // 1

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

Promise.resolve().then(function() {
  console.log('promise'); // 4
});

console.log('end'); // 2

这段代码中,我们依次执行了以下操作:

打印"start"
打印"end"
创建一个Promise对象,并将其添加到微任务队列中
执行Promise中的回调函数,打印"promise"
执行setTimeout中的回调函数,打印"setTimeout"
根据JavaScript事件循环机制的规则,它的执行过程如下:

全局上下文入栈,开始执行同步任务
打印"start"
全局上下文出栈
全局上下文入栈,开始执行同步任务
打印"end"
全局上下文出栈
全局上下文入栈,开始执行同步任务
创建Promise对象,并将其添加到微任务队列中
执行Promise对象中的回调函数,打印"promise"
全局上下文出栈
执行微任务队列中的任务,打印"setTimeout"
因此,最终输出结果应该是:

start
end
promise
setTimeout

6, What is the output of the following code?

console.log(1);

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

Promise.resolve().then(() => {
  console.log(3);
});

console.log(4);

输出结果是 1 4 3 2。

解析:代码执行顺序为同步任务先执行,输出1,然后将异步任务放入任务队列中。setTimeout是一个宏任务,Promise.then是一个微任务。由于Promise是微任务,会优先执行,所以先输出3。然后执行完同步任务后,会依次执行微任务。所以输出顺序为1,4,3。最后执行宏任务,输出2。

7, What is the output of the following code?

console.log("start");

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

Promise.resolve().then(() => {
  console.log("Promise");
});

console.log("end");

输出结果是 start end Promise setTimeout。

解析:代码执行顺序同第一题,先输出同步任务 start 和 end,然后将异步任务放入任务队列中。由于Promise.then是一个微任务,所以会优先执行,输出 Promise。接着执行完同步任务后,依次执行微任务中的 Promise。最后执行宏任务 setTimeout 输出结果。

13. Process records

Record 1. Browser event loop mechanism or JavaScript event loop mechanism?

Viewpoint 1

The browser event loop mechanism and the JavaScript event loop mechanism are the same concept, but they have different names.
The JavaScript event loop mechanism is divided into browser and Node event loop mechanisms, and the implementation technologies of the two are different. Browser Event Loop is a specification defined in HTML, and Node Event Loop is implemented by the libuv library. The JavaScript event loop mechanism is divided into two parts: the JS call stack and the task queue. The JS call stack is a last-in-first-out data structure. When a function is called, it is added to the top of the stack. After execution is completed, the function is removed from the top of the stack until the stack is cleared. The task queue is a first-in, first-out data structure. When the main thread is idle, it will read a task from the task queue in order and put it on the stack for execution.

Viewpoint 2

The browser event loop mechanism and the JavaScript event loop mechanism are closely related. The browser event loop mechanism refers to how the browser coordinates and executes various events when processing various events (such as user interaction, network requests, timers, etc.). The JavaScript event loop mechanism refers to how the JavaScript engine coordinates the execution of asynchronous code when processing it. In fact, the JavaScript event loop mechanism is built on the browser event loop mechanism. The JavaScript engine registers an event listener by calling the API provided by the browser, and then adds the callback function to the task queue when the event is triggered. The browser event loop mechanism will continuously take out callback functions from the task queue and execute them until the task queue is empty. Therefore, the browser event loop mechanism can be considered the basis of the JavaScript event loop mechanism.

14. Welcome exchanges and corrections

15. Reference links

JavaScript - Brief Analysis of Event Loop Mechanism - Nuggets

https://www.cnblogs.com/kitebear/p/17400025.html

Detailed explanation: JavaScript event loop mechanism (event loop), js running mechanism (execution sequence)_javascript event loop_Chen Acridine's blog-CSDN blog

Web front-end tips: js event loop (Event Loop)

Event Loop event loop mechanism in JavaScript

Event Loop Mechanism (EventLoop)_Event Loop Principle_Xingyu H's Blog-CSDN Blog

Guess you like

Origin blog.csdn.net/snowball_li/article/details/125236024