What this article brings to you is about how JavaScript event rings should be understood. (text), there is a certain reference value, the need for friends can refer to, I hope to help you.
Stacks and queues
Accessing data in computer memory, the basic data structure is divided into stacks and queues.
Stack is a last-in-first-out data structure, note that sometimes the stack is called "stack", but "heap" is another complex data structure, it is completely different from the stack. The stack is characterized by the operation only at one end, generally speaking, the stack operation only two: the Stack and the stack. The first data in the stack is always the last one to come out.
A queue is similar to a stack, but it is an FIFO data structure that inserts data from one end of the queue while the operation is deleted at the other end.
The popular analogy stack is like a good bucket, the data placed in the stack will be placed at the bottom of the bucket, the data will be taken out of the bucket at the end of the stack, so the first data placed in the stack is always the last to be taken out. The queue is like a water pipe, and the first data that is put in the queue flows from the other end of the queue, which is the biggest difference.
In JavaScript, the execution of a function is a typical process of going into and out of the stack:
function fun1 () { function fun2 () { function fun3 () { console.log (' Do it '); } Fun3 (); } Fun2 ();} Fun1 ();
In the execution of the program, first the fun1,fun2,fun3 into the stack, and in the call function, is the first call FUN3 (out of the stack), then fun2 and fun1, imagine, if fun1 first out of the stack, then the function fun2 and FUN3 will be lost.
Single-threaded and asynchronous
In JavaScript, the program is single-threaded and has only one main thread, which is why? Because it is not difficult to imagine that the original JavaScript was designed to run in the browser script language, if the design of multi-threading, two threads at the same time to modify the DOM who will prevail? So JavaScript is a single-threaded, in a thread in the code will go down a sentence, until the program runs out, if the middle of a more time-consuming operation, it can only wait.
Single-threaded design makes language execution inefficient, in order to take advantage of the performance of multi-core CPUs, the JavaScript language supports asynchronous code, and when there is a more time-consuming operation, the task can be written as asynchronous execution, when an asynchronous task is not finished, the main thread will suspend the asynchronous task, Continue with the subsequent synchronization code, and then look back, if an asynchronous task runs out and then executes it.
This kind of code execution in fact very much in line with our life in many scenes, such as Xiao Ming's classmate came home from work, he is thirsty, want to boil water, if it is a synchronous way of execution that is to boil, in the waters did not open when Xiao Ming like a fool wait, and so the water opened the tea; Then go to do something else, such as watching TV, listening to music, and so on the water boil and then go to tea. The second type of asynchronous approach is significantly more efficient.
What are the common asynchronous operations? There are a number of common ones that we can list:
Let's look at a piece of code first:
Example 1console.log (1); SetTimeout (function () { console.log (2);}, +); Console.log (3);
This code is very simple, put them in the browser execution results are as follows:
132
Because the settimeout function is delayed by 1000 milliseconds, it is logical to output 1 and 3, and 2 to output after 1000 milliseconds.
Let's change the code a little bit and change the settimeout delay time to 0:
Example 2console.log (1); SetTimeout (function () { console.log (2);}, 0);//0 milliseconds, no delay console.log (3);
Operation Result:
132
Why is the delay 0 milliseconds or the last output of 2? Don't worry, let's look at the code again:
Example 3console.log (1); SetTimeout (function () { console.log (2);}, 0); Promise.resolve (). Then (function () { console.log (3);}); Console.log (4);
Operation Result:
1432
The above three pieces of code, if you can write the results correctly, and can say why the output, that you understand the JavaScript event ring understanding is very clear, if not, we will talk about what happened in this, actually very interesting.
How is JavaScript executed?
Let's start with a brief chat about the basic data structure, what does it have to do with the event loop we're talking about? Of course, the first thing to make clear is that theexecution of JavaScript code is all in the stack , whether it is synchronous or asynchronous code, this must be clear.
And the code we basically divided into synchronous code and asynchronous code, in fact, the asynchronous code can be divided into two categories: macro Tasks and micro-tasks .
Forget what is a macro task and micro-task, often this high-level terminology is not conducive to our understanding, we first think: macro, that is, macro, large, micro-micro, small.
JavaScript is an interpreted language, and it does so in the following way:
Explain each JS statement from top to bottom in turn
If the synchronization task is pressed into a stack (the main thread), and if it is an asynchronous task, put it in a task queue
Start the synchronization task in the stack until all the tasks in the stack are finished, and the stack is emptied
Look back at the asynchronous queue if an asynchronous task is completed, generate an event and register the callback, pressed into the stack
Return to the 3rd step until the asynchronous queue is emptied, and the program runs to the end
Language description of the laborious, rather than look at the picture:
The above steps can be seen, whether synchronous or asynchronous, as long as the execution is performed in the stack, and over and over again to check the asynchronous queue, this execution is called "event ring."
Understanding the implementation of JavaScript, it is not difficult to understand the previous second code, why settimeout is 0 when the last execution, because settimeout is an asynchronous code, you must wait until all the synchronization code is executed, the asynchronous queue will be executed. Even if the settimeout executes faster, it is not possible to execute before synchronizing the code.
Event loops in the browser
Chatting so much, we seem to have not said the macro task and micro-task of the topic, said, the asynchronous task is divided into micro-tasks and macro tasks, then they are a kind of implementation mechanism?
Attention! Micro tasks and macro tasks are performed differently in the browser and node , and there are differences! The important thing we say several times, the following discussion is in the browser environment.
in the browser execution environment, always perform small, micro tasks, then perform large, macro tasks , back to look at the third piece of code, why promise then method before settimeout execution? The fundamental principle is that the promise then method is a micro task, and settimeout is a macro task.
Next we borrow a picture from teacher Nanyi to illustrate:
In fact, the above diagram we can refine it a little bit, the image of the asynchronous queue only one, that is, no distinction between the micro-task queue and the macro task queue. We can fill in the brain, add a micro-task queue in this diagram, when JavaScript execution, add another judgment, if the micro-task is added to the micro-task queue, the macro task is added to the macro task queue, when the queue is emptied, the browser will always prioritize the "micro-task." In this way, the browser's event loop is completely removed.
Finally, a big test, the following code to run the result is:
<script type= "Text/javascript" > setTimeout (function () { console.log (1); Promise.resolve (). Then (function () { console.log (2); }); }); SetTimeout (function () { console.log (3); }); Promise.resolve (). Then (function () { console.log (4); }); Console.log (5);</script>
Copy this code to chrome and run, and the result is:
54123
Let's try to analyze why this is the result, first output 5, because it console.log(5)
is synchronous code, there is nothing to say.
Then put the first two settimeout and the last promise into the asynchronous queue, pay attention to their distinction, at this time after executing the synchronization code found in the micro-task and Macro task queue has code, according to the browser's event loop mechanism, the priority of performing micro-task, at this time output 4.
Then execute the first settimeout in the macro task queue, Output 1.
At this point, there is another promise in settimeout, put into the micro-task queue.
Empty the Micro task queue again, output 2.
The final macro task queue also has the last settimeout, Output 3.
The event Loop in node
The event loop in node is somewhat different from the browser, and has a special description in the official file of node. js, where there is a diagram detailing its event loop mechanism, which we take out:
As you can see, the event loop mechanism in node. JS is divided into 6 phases, the most important of which are 3 phases I noted above:
Timer stage, refers to the settimeout and other macro tasks
Poll polling phase, such as reading files and other macro tasks
Check stage, Setimmediate macro task
Each stage in the diagram represents a macro task queue, in which the micro task runs when each "macro task queue" is emptied and is executed between the next macro task queue. This is the biggest difference from the browser.
Or use the code to speak, there is a classic node. js Event Ring Interview question:
Const FS = require (' FS '); Fs.readfile ('./1.txt ', (err, data) = { SetTimeout (() = { Console.log (' timeout ') ); }); Setimmediate (() = { Console.log (' immediate '); }); Promise.resolve () and then (() = { Console.log (' Promise ');});
Operation Result:
Promiseimmediatetimeout
The code is not complicated, first read a file using the FS module, there are two macro tasks and a micro task inside the callback, the micro task is always better than the macro task execution, so first output promise.
But then why did the difference first output immdiate? The reason is that the FS read the file's macro task in the 4th polling phase, when the 4th phase clears the queue, it is time to enter the 5th check phase, that is, setimmediate this macro task in the phase, and does not jump back to the 1th phase, so first output immedate.
Tail
Finally, it is not easy to analyze the browser and node event loops, but you can analyze the results by remembering the differences between them.
The browser event loop is a macro task that clears the Micro task queue immediately after the operation.
The node event loop empties the Micro task queue after emptying one stage of the macro task queue .
Finally, summarize common macro tasks and micro-tasks:
Macro Task |
Micro Task |
SetTimeout |
The then method of promise |
SetInterval |
Process.nexttick |
Setimmediate |
Mutationobserver |
Messagechannel |
|