Lecture
What do you think will happen if you run this piece of code in the browser console?
function foo() { setTimeout(foo, 0); } foo();
And this one?
function foo() { Promise.resolve().then(foo); } foo();
If you, like me, have read a bunch of articles about Event Loop, Main Thread, tasks, micro-drags, etc., but find it difficult to answer the questions above, this article is for you.
So let's get started. The code for each HTML page in the browser is executed in the Main Thread . The Main Thread is the main thread where the browser does JS, does redraws, handles custom actions, and more. Basically, this is where the JS engine is integrated into the browser.
The easiest way to figure it out is by looking at the diagram:
Figure 1
We see that the only place through which tasks can get into the Call Stack and be executed is the Event Loop. Imagine that you are in his place. And your job is to keep up with the tasks. Tasks can be of two types:
Most likely, your personal tasks will be prioritized. Event Loop agrees with this :) It remains to streamline the tasks from the customer.
Of course, the first thing that comes to mind is to give each customer a priority and line them up. The second is to determine how exactly the tasks from each customer will be processed - one at a time, all at once, or maybe in batches.
Let's take a look at this diagram:
Figure 2
Based on this diagram, all the work of the Event Loop is built.
After we have started executing any script, a task with the execution of this script is put into the Tasks queue. As this code is executed, we come across tasks from different customers that are put in the appropriate queues. After the task of executing the script is completed (task from Tasks), Event Loop goes to Microtasks (after the task from Tasks, Event Loop takes tasks from Microtasks). Event Loop takes tasks from him until they finish . This means that if the time they were added is equal to the time they were executed, then the Event Loop will rake them endlessly.
Then he goes to Render and performs tasks from him. Render tasks are optimized by the browser, and if it thinks that nothing needs to be redrawn in this loop, then Event Loop will simply go further. Then Event Loop again takes tasks from Tasks and asks him for only one, the first task in the queue , transfers it to CallStack and goes further through the loop.
If one of the customers didn't have any tasks, then Event Loop just goes to the next one. Conversely, if a customer's tasks take a long time, then other customers will wait for their turn. And if the tasks from some customer turned out to be endless, then the Call Stack overflows, and the browser begins to swear:
Figure 3
Now that we understand how the Event Loop works, it's time to figure out what happens after executing the code snippets at the beginning of this article.
function foo() { setTimeout(foo, 0); } foo();
We can see that the foo function calls itself recursively via setTimeout internally, but each time it is called, it creates a Tasks customer task. As we remember, in the Event Loop, when executing the task queue from Tasks, it takes only 1 task per loop. And then tasks from Microtasks and Render are performed. Therefore, this piece of code will not make Event Loop suffer and rake its tasks forever. But it will throw up a new task for the customer Tasks on each lap.
Let's try to execute this script in the Google Chrome browser. To do this, I created a simple HTML document and connected script.js with this piece of code in it. After opening the document, go to the developer tools, and open the Perfomance tab and click the 'start profiling and reload page' button there:
Figure 4
We see that tasks from Tasks are executed one by one per cycle, approximately once every 4ms.
Consider the second problem:
function foo() { Promise.resolve().then(foo); } foo();
Here we see the same thing as in the example above, but the foo call adds tasks from Microtasks, and they are all executed until they finish. And this means that until Event Loop finishes them, he will not be able to move to the next customer :( And we see again a sad picture .
Let's take a look at this in the development tools:
Figure 5
We see that microtasking is executed approximately once every 0.1ms, and this is 40 times faster than the Tasks queue. All because they are executed all at once. In our example, the queue moves infinitely. For rendering, I reduced it to 100,000 iterations.
That's all!
I hope this article was useful to you, and now you understand how the Event Loop works and what is 'going on' in the code examples above.
Comments
To leave a comment
Scripting client side JavaScript, jqvery, BackBone
Terms: Scripting client side JavaScript, jqvery, BackBone