Home > Article > Web Front-end > Completely master the JavaScript execution mechanism
This article brings you related issues about the JavaScript execution mechanism, including knowledge about JavaScript single-threading and JavaScript synchronization and asynchronousness. I hope it will be helpful to everyone.
If we want to understand why JavaScript is single-threaded, we have to start with what JavaScript is used to do. Come and get started.
As the scripting language of the browser, the purpose of JavaScript output is to allow the browser to interact with the user and operate DOM elements, thereby improving the user's interaction and experience. JavaScript needs to operate the browser's DOM element, so JavaScript cannot become a multi-threaded language. Let's assume a scenario. If JavaScript is a multi-threaded language, two threads operate a DOM element at the same time. One thread needs to edit and update the DOM element, while the other thread needs to edit and update the DOM element. It is to delete the DOM element node. Which one should the browser use?
You can only do the same thing at the same time. Because of the operation of DOM elements, single threading is the core and characteristic of the JavaScript language.
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 must not operate the DOM. Even such changes do not change the single-threaded nature of js.
The single-thread mechanism of JavaScript results in that only one thing can be done at the same time. Just like a bunch of people withdrawing money from an ATM machine, even if there are more people behind them who are in a hurry, they can only queue up one by one and wait for the first person to withdraw money before it is the turn of the next person.
But this will cause that if the previous task takes too long, the next task will wait for a very long time. For example, if we need to load an Ajax request with a very large amount of data, we have to wait for the corresponding result of the request. , and then continue to perform subsequent tasks.
So how should we deal with this situation? Since we cannot change the single-thread mechanism of JavaScript, can we temporarily suspend some long-term tasks and wait until the main task is completed before executing these mounted tasks? The author of JavaScript also thought of this way. JavaScript has synchronous tasks and asynchronous tasks.
Synchronous tasks mean that tasks are queued on the main thread, and the next task must wait for the completion of the previous task before it can be executed. An asynchronous task means that the task does not enter the main thread, but enters the task queue to wait. The task that enters the task queue will only enter if the "task queue" notifies the main thread that an asynchronous task can be executed. Main thread execution.
All synchronization tasks of JavaScript are executed on the main thread, forming an execution stack.
The task queue is based on the first-in, first-out principle. Events in the advanced queue are executed first, and events in the last queue are executed later.
Event Loop
Execute synchronization tasks in the execution stack.
When encountering an asynchronous task, it will not wait for its return result. It will temporarily suspend the asynchronous task and continue to execute other synchronous tasks.
When the asynchronous task has results, the JavaScript task will be added to the task queue. Tasks added to the task queue will not be executed immediately, but will wait for the main thread (execution stack) to be idle before being added to the execution stack for callback execution.
Waiting for the execution stack The task is completed.
Add the task in the task queue to the execution stack and execute it.
Repeatedly, this forms an infinite loop (event loop). (The picture below is from the Internet)
After learning Event Loop, let’s take a look at the following question:
setTimeout(function(){ console.log('setTimeout start') }); new Promise(function(resolve){ console.log('promise start'); resolve() }).then(function(){ console.log('promise then') }); console.log('script end');
Try to follow, we just learned above To analyze the JavaScript execution mechanism
1. First execute the synchronous task until setTimeout, but setTimeout is a temporary suspension of the asynchronous task, waiting for the timer to time out, and adding it to the task queue.
2. Continue until new Promise is executed. New Promise contains synchronization tasks and prints "promise start".
3. Add .then to the task queue after executing resolve.
4. Print "script end" after executing console.log('script end');
5. At this time, the main task has been executed. Add the asynchronous task to the main task and execute it directly, print "setTimeout start", then add .then to the main task, and print "promise then" .
So the result should be: promise start -> script end -> setTimeout start -> promise then?
After executing it in the browser personally, the result is not like this, but promise start -> script end -> promise then -> setTimeout start
Then why are the results above inconsistent with what we expected, and why setTimeout start is printed after the promise.
In fact, it is because asynchronous execution is also sequential. In fact, it is inaccurate to use asynchronous and synchronous methods to divide the execution order of task queues. It should be divided into microtasks and macrotasks.
Macro-task: script code, setTimeout, setInterval
Micro-task: Promise, process. nextTick
So setTimeout is a macro task in asynchronous tasks, and Promise is a micro task in asynchronous tasks. Whether it is a microtask or a macrotask, it will enter the corresponding Event Queue. Next we are looking at a flow chart.
Let’s understand it a little bit:
1. Execute macro task (script code)
2. When a microtask is encountered when executing a macrotask, the microtask will be added to the Event Queue
3. After the current macrotask is completed, it will be viewed Event Queue of the microtask, and execute all the microtasks in sequence
4. After executing all the microtasks, continue to the first step, and this cycle
This is also the running mechanism of javaScript. Combined with this, we will re-analyze the above example.
1. First execute the macro task (script code), and when setTimeout is encountered, add it to the Event Queue of the macro task.
2. Continue to execute new Promise and print "promise start".
3. After executing to resolve, .then is the microtask and is added to the Event Queue of the microtask.
4. Print "script end" after executing console.log('script end');
5. At this point, all the macro tasks of this round have been executed. At this time, check whether there are executable micro tasks in the Eevent Queue of the micro tasks. It is found that there are executable micro tasks just in the third Step 1: Add the amount.then, execute and print "promise then"
6. At this time, the first round of event loop has completely ended, and the next round of event loop will execute an Macro task, it is found that the setTimeout is added to the Event Queue of the macro task, execute and print "setTimeout start"
Always remember that JavaScript is single-threaded, it was before, is now, and will be in the future It will be. All talk about multi-threading is bullshit.
Even Event Queue is not only a way to achieve asynchronous implementation, but also an execution mechanism of js.
It can be implemented using JavaScript in the future. All will be implemented using JavaScript.
Related recommendations: javascript learning tutorial
The above is the detailed content of Completely master the JavaScript execution mechanism. For more information, please follow other related articles on the PHP Chinese website!