Soul three q
- Why is JavaScript single threaded?
- Why does JavaScript need asynchrony?
- How can JavaScript single threads be asynchronous?
First: Why is JavaScript single-threaded?
Suppose you have two threads, Process1 and process2, and the JavaScript is multithreaded, so they can operate on the same DOM at the same time. If Process1 deletes the DOM and Process2 edits it, and there is a conflict between threads, how exactly does the browser perform? With that in mind, it’s easy to understand why JavaScript is designed to be single-threaded.
Second: Why does JavaScript need to be asynchronous?
Assuming there is no asynchrony in JavaScript, it can only be executed top-down, and if the previous line takes a long time to parse, the code below will block. Blocking means “stuck” to the user, which leads to a poor user experience, hence asynchronous execution in JavaScript.
3. How do JavaScript single threads achieve asynchrony?
This is done through an event loop
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.
Task queue
When an asynchronous event is encountered, the JavaScript engine does not wait for the asynchronous event to return the result. Instead, it hangs the event in a separate queue from the execution stack, called a task queue.
These tasks are subdivided into macro tasks and micro tasks
Macrotask: Script (global task), setTimeout, setInterval, setImmediate (Node.js), I/O (disk read/write or network communication), UI rendering (UI interaction events)
Microtasks: process.nexttick (unique to Node.js), promise.then, MutationObserver
Event loop
The first thing to be clear about is that a browser is a process with multiple threads
Typically, browsers have the following five threads:
- GUI rendering thread
- JavaScript engine threads
- The browser event triggers the thread
- Timer trigger thread
- Asynchronous HTTP request threads
The GUI rendering thread and JavaScript engine thread are mutually exclusive, and other threads can execute in parallel with each other.
In the browser, the JavaScript engine reads and executes tasks from the task queue in a loop called an event loop.
The event loop follows the following steps:
- Execute synchronized code, which is a macro task (the initial synchronized code is the overall script code)
- If the execution stack is empty, check whether microtasks need to be executed
- Perform all microtasks
- Render the UI if necessary
- Then start the next Event loop, which executes the asynchronous code in the macro task
Examples:
console.log('script start');
async function async1() {
await async2()
console.log('async1 end');
}
async function async2() {
await async3()
console.log('async2 end');
}
async function async3() {
console.log('async3 end');
}
async function Myasync1() {
await Myasync2()
console.log('Myasync1 end');
}
async function Myasync2() {
await Myasync3()
console.log('Myasync2 end');
}
async function Myasync3() {
console.log('Myasync3 end');
}
async1()
Myasync1()
setTimeout(function() {
console.log('setTimeout');
}, 0)
new Promise(resolve= > {
console.log('Promise');
resolve()
})
.then(function() {
console.log('promise1');
})
.then(function() {
console.log('promise2');
})
console.log('script end');
Copy the code
The correct answer is :script start-> asynC3 end->Myasync3 end->Promise->script end-> asynC2 end->Myasync2 end->promise1-> asynC1 end->Myasync1 end->promise2->setTimeout
The explanation is as follows:
- Execute global task: script Prints script start
- When the synchronous code async1() forms a microtask queue, the order of the microtasks is as follows :async3() -> async2() -> async1(), async()3 will be executed and async()3 end will be printed. Then the microtask queue surrenders control and continues to execute the code
- When the synchronous code Myasync1() forms a microtask queue, The microtasks are sorted as follows :Myasync3() -> Myasync2() -> Myasync1(), at which point Myasync()3 is executed and Myasync()3 end is printed, and the microtask queue surrenders control and continues to execute the code
Since the timing function is a macro task, whether to call it or not will be considered after the execution of the micro-task is completed. Therefore, setTimeout will not be executed immediately because the time is 0
- In the case of synchronous code New Promise, a microtask queue is formed, The microtasks are sorted as follows :Promise() -> promise.then () -> promise.then (), at which point the Promise is executed and Promise 1 is printed, and the microtask queue relinquishes control and continues executing the code
- Encountered synchronization code console.log(‘script end’); The output script end
At this point, the first script macro task is finished. Since this macro task generates microtasks, all the microtasks will be executed before the next macro task is started
Async -> MyAsync -> promise.then ()
The microtask queue is executed in a loop, and once executed by the current microtask queue, control is handed over to the next microtask queue
- async2 end
- Myasync2 end
- promise1
- async1 end
- Myasync1 end
- promise2
When output of promise2, all queues of microtasks generated by the first macro task are executed. SetTimeout is executed and setTimeout is printed
Refer to the link