JavaScript single thread
JavaScript is single-threaded, which means you can only do one thing at a time.
So why can’t JavaScript have multiple threads?
As a browser scripting language, JavaScript’s primary purpose is to interact with users and manipulate the DOM. This means that it has to be single-threaded, which can cause complex synchronization problems. For example, if there are two threads of JavaScript at the same time, one thread adds content to a DOM node, and the other thread removes that node, which thread should the browser use?
So, to avoid complexity, JavaScript has been single-threaded since its inception, and this has been a core feature of the language and will not change.
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.
So, due to the single-threaded nature of JS, if js makes an asynchronous IO request, the rest of the code will block while waiting for the result to return.
We know that the JS main thread and renderer block each other, so this can cause the browser to feign death. How to solve this problem? One effective way to do this is the Event Loop that we’ll talk about in this section.
What is the event loop?
All tasks in JS are classified into two types: synchronous task and asynchronous task asynchronous
- A synchronization task refers to a task that is immediately executed. The synchronization task is usually directly executed in the main thread.
- Asynchronous tasks refer to asynchronously executed tasks, such as Ajax network requests and setTimeout timing functions. Asynchronous tasks are coordinated through the task queue mechanism (first-in, first-out mechanism)
Asynchronous tasks are divided into macro tasks and micro tasks
- Macro task:
SetTimeout and setInterval requestAnimationFrame belong to the following categories: JS I/O, UI MessageChannel, postMessage setImmediate The GUI engine, which occurs during the redraw and rearrangement portion of the rendering process, is executed prior to UI rendering
- Micro tasks:
Process.nexttick (node.js environment) MutaionObserver (browser environment) Promise callback
Js runtime mechanism:
- 1. Extract a task from the header of the macro task to execute;
- 2. Add microtasks to the microtask queue if they are encountered during execution;
- 3. After the macro task is executed, check whether there are tasks in the micro task queue. If there are tasks, execute them one by one until the execution is complete.
- 4. GUI rendering;
- 5. Repeat Step 1 until the macro task is complete.
The first four steps form a loop detection mechanism for events, known as eventloop.
3. Execution sequence of macro task and micro task
console.log(1); setTimeout(function() { console.log(2); }, 0); new Promise(function(resolve) { console.log(3); resolve(Date.now()); }).then(function() { console.log(4); }); console.log(5); setTimeout(function() { new Promise(function(resolve) { console.log(6); resolve(Date.now()); }).then(function() { console.log(7); }); }, 0); // 1. Run log(1) to output 1. // 2. When setTimeout is encountered, add the code log(2) of the callback to the macro task for execution; // 3. Run console.log(3) to add log(4) from then to the microtask; // 4. Run log(5) to output 5. // 5. When setTimeout is encountered, add the code log(6, 7) for the callback to the macro task; Check whether there is a task in the microtask queue. There is a microtask log(4) (added in Step 3), and go to output 4. // 7. Execute the next macro task log(2), output 2; // 8. After one task of the macro task is executed, check whether there is a task in the microtask queue. // 9. Execute the next macro task, execute log(6), add the log(7) from then to the microtask; // 10. After executing the macro task, there is a microtask log(7) (added in Step 9), execute output 7; // Therefore, the final output order is: 1, 3, 5, 4, 2, 6, 7; / / reference https://juejin.cn/post/6844904035770695693Copy the code
Message queue Message queue MicroTask queue
- 1. The execution context is created before the function is executed, and the function is pushed onto the call stack during execution
- 2. If an asynchronous task is a macro task, the callback function of the macro task is put into a message queue for execution
- 3. If it is a microtask, the callback function of the microtask is put into the microtask queue for execution
- 4. When the call stack is emptied, tasks in the microtask queue will be pushed into the call stack for execution and pop-up successively. After the microtask queue is emptied, tasks in the message queue will be pushed into the call stack for execution and pop-up successively
Reference: www.bilibili.com/video/BV1kf…
5. Event loops in Node.js
1. Features of event loops in Node.js:
Node.js handles the interaction with the operating system through Libuv and is thus asynchronous, non-blocking, event-driven.
Node.js is actually a single thread of Javascript execution thread, the real I/O operations, the underlying API calls are performed through multiple threads. CPU intensive tasks are the Achilles heel of Node.js.
Reference: developer.aliyun.com/article/320…
2. Differences with browser event loop
The event loop in Node.js behaves in much the same state as the browser.
The difference is that Node has its own model. The implementation of event loops in Node relies on the Libuv engine.
The single Loop of the Event Loop in Node.js is staged.
Each phase does not proceed to the next phase or the specified phase until the last phase is completed and the next loop is entered. In each phase, except the Poll phase, Node executes all macro tasks corresponding to that phase, and then executes all tasks in the microtask queue.
Note: In Node.js 11.0 and later, Goole changes this behavior to browser-consistent, meaning that after each Macrotask(setTimeout,setInterval, and setImmediate) executes, The code in the following example is parsed to the latest.
3. Event cycle model
4. Execution sequence of event loops in NodeJS
External input data –>
Poll stage –>
Check phase –>
Close the event callback phase –>
Timer check phase –>
I/O callbacks –>
Idle, prepare –>
Polling phase… The general functions of these stages are as follows:
- Timer detection phase (
timers
): This phase performs callbacks in the timer queue as shown insetTimeout()
和setInterval()
.
I/O event callback phase (I/O Callbacks): This phase performs almost all callbacks. But not close events, timers, and setImmediate() callbacks.
- Idle stage (
idle
.prepare
): This stage is for internal use only and can be ignored
Poll phase (poll): Waiting for new I/O events. Node blocks here in some special cases.
- Inspection phase (
check
) :setImmediate()
The callback is executed during this phase.
Close callbacks: for example socket.on(‘close’,…) The callback to this close event
- Poll:
This phase is polling time, waiting for I/O events that have not yet returned, such as server responses, user mouse movements, and so on. This phase will take a long time. If there are no other asynchronous tasks to be processed (such as an expired timer), it stays in this phase, waiting for the I/O request to return the result.
- check:
This phase executes the setImmediate() callback function.
- close:
This phase executes the callback function that closes the request, such as socket.on(‘close’,…). .
- The timer phase:
This is the timer phase, which handles callbacks to setTimeout() and setInterval(). After entering this phase, the main thread checks the current time to see if the timer conditions are met. If so, execute the callback, otherwise leave the phase.
- I/O callback phase:
All but the following call-back functions are executed at this stage: setTimeout() and setInterval() call-back functions setImmediate() are used to turn off the requested callback, such as socket.on(‘close’,…)
Reference: segmentfault.com/a/119000003… Segmentfault.com/a/119000002…