Single thread and multi-thread
Javascript is a single-threaded, asynchronous, non-blocking, parse-type scripting language.
JavaScript is designed to handle the interaction of a browser’s web pages (DOM manipulation processing, UI animation, etc.), making it a single-threaded language. If there are multiple threads working on the DOM at the same time, the web page will be a mess.
So, JS processes tasks one by one, from top to bottom.
Log (' start ') console.log(' middle ') console.log(' end ') // Start // Middle // endCopy the code
However, if a task takes too long to process (or wait for) a network request, timer, waiting for a mouse click, and so on, subsequent tasks are blocked, which means blocking all user interaction (buttons, scrollbars, and so on), making for an extremely unfriendly experience. Such as:
Console. log(' start ') console.log(' middle ') setTimeout(() => {console.log('timer over')}, 1000) console.log(' end ') // start // middle // end // timer overCopy the code
The timer over prints after the print is complete, meaning that the timer does not block subsequent code.
So, in fact, JavaScript single thread refers to the browser is responsible for interpreting and executing JavaScript code only one thread, namely the JS engine thread, but the browser rendering process provides multiple threads, as follows:
- JS engine thread
- Event trigger thread
- Timing trigger thread
- Asynchronous HTTP request threads
- GUI rendering thread
In summary: When a timer, DOM event listener, or network request is encountered, the JS engine sends them directly to the corresponding thread of the browser for processing, and the JS continues to process the subsequent task, thus implementing asynchronous non-blocking. After other threads finish processing, the corresponding callback function will be handed over to the message queue maintenance, and the JS engine will fetch the task and execute it at the appropriate time.
Event loops and message queues (also called task queues)
As mentioned above, the JS engine will also fetch the task from the message queue and execute it at the appropriate time. This is where the event loop mechanism comes in.
The event loop mechanism and the maintenance of message queues are controlled by the event-triggering thread.
JS engine threads encounter asynchrony (DOM event listeners, network requests, setTimeout timers, etc.) , the corresponding thread is left alone to maintain the asynchronous task, waiting for a certain moment (the timer ends, the network request succeeds, the user clicks DOM), and then the thread is triggered by an event to add the corresponding callback function to the message queue, which is waiting to be executed.
At the same time, the JS engine thread will also maintain an execution stack, synchronous code will be successively added to the execution stack execution, the end will exit the execution stack.
Only when the execution stack is empty (i.e. the JS engine thread is idle) will the event-triggering thread pull a task (i.e. an asynchronous callback function) from the message queue into the execution stack to execute.
1. All synchronization tasks are executed on the main thread, forming an execution context stack. 2. Besides the main thread, there is a Task queue. Whenever an asynchronous task has a result, an event is placed in the "task queue". 3. Once all synchronization tasks in the Execution stack are completed, the system reads the Task queue to see what events are in it. Those corresponding asynchronous tasks then end the wait state, enter the execution stack, and start executing. 4. The main thread repeats step 3 above. Whenever the main thread is empty, it reads the "task queue", that's how JavaScript works. This process is repeated over and over again. This mechanism is called an event loop mechanism.Copy the code
(Asynchrony generally includes: network request, timer, DOM event listener)
Macro and micro tasks
console.log('script start')
setTimeout(function() {
console.log('timer over')
}, 0)
Promise.resolve().then(function() {
console.log('promise1')
}).then(function() {
console.log('promise2')
})
console.log('script end')
// script start
// script end
// promise1
// promise2
// timer over
Copy the code
Q: “Promise 1” “Promise 2” printed before “timer over”? A: This introduces a new concept: macro-task and micro-task.
Macro-task:
An Event loop has one or more task queues. Task task sources are very broad, such as Ajax onload, click events, basically all kinds of events that we often bind are task task sources, and database operations (IndexedDB), Note that setTimeout, setInterval, and setImmediate are also task sources. To sum up, task
- setTimeout
- setInterval
- setImmediate
- I/O
- requestAnimationFrame
- UI rendering
Micro-task:
A MicroTask queue is similar to a task queue in that it is a first-in, first-out queue and the task is provided by the specified task source. The difference is that there is only one MicroTask queue in an Event loop. In addition, there are differences between microtask execution timing and Macrotasks, mainly including:
- process.nextTick
- promises
- Object.observe
- MutationObserver
(In a Node environment, process.nextTick takes precedence over promises, meaning that the nextTickQueue in the microtask queue is executed after the macro task ends before the Promise in the microtask is executed.)
The difference between the two:
- There can be multiple macro queues and only one microtask queue. Therefore, every new setTimeout created is a new macro task queue. After executing a macro task queue, checkpoint microtask is performed.
- After an event loop, the microtask queue completes and the macro task queue executes
- An event loop checks the microtask queue after executing a macro queue.
Execution mechanism:
- The JS engine thread executes a main block of code (fetched from the event queue if not in the stack)
- If a microtask is encountered during execution, it is added to the task queue of the microtask
- 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 engine thread proceeds to the next macro task (retrieved from the macro task queue)
conclusion
A TAB is a process, and a process contains multiple threads, so multiple threads will be called to process the page during rendering. What we say is the characteristic of JS single thread, in fact, refers to the execution and processing of JS thread only one, is the JS engine thread. When a page rendering process will have some event processing, timer, asynchronous request, GUI page rendering etc., these JS will hand them to the corresponding thread for processing, after the corresponding thread processing, will generate the corresponding callback function, these callback functions will be temporarily stored in the message queue. When the macro task in the main thread (JS engine thread) and the micro task generated by it are processed, that is, when the main thread is empty, it will go to the message queue to pick up the callback function for processing. After a callback function has been processed, the JS thread will fetch the callback function from the message queue again and again until everything has been processed. This is also called an event loop.
Reference: zhuanlan.zhihu.com/p/139967525