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, microtasks and more, 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
Main Thread . Main Thread is the main thread where the browser performs JS, does redraws, handles user actions, and much more. Essentially, this is where the JS engine is integrated into the browser.
')
The easiest way to figure it out is by looking at the diagram:
Picture 1We see that the only place through which tasks can get into the Call Stack and complete is Event Loop. Imagine that you were in his place. And your job is to keep up with the "rake" tasks. Tasks can be of two types:
- Personal - execution of the main JavaScript code on the site (hereinafter we will assume that it has already been executed)
- Customer Tasks - Render, Microtasks, and Tasks
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 exactly how the tasks from each customer will be processed - one at a time, all at once, or maybe in batches.
Take a look at this diagram:
Figure 2Based on this scheme, the entire work of Event Loop is built. After the Call Stack becomes empty (personal tasks have ended), Event Loop first of all goes to the Tasks customer and asks him for
only one, the first task in the queue , transfers it to CallStack and goes on. The next customer is Microtasks. Event Loop takes tasks
from it until they are finished . This means that if the time of their addition is equal to the time of their execution, then the Event Loop will rake them endlessly.
Next, he goes to Render and performs tasks from him. The tasks from Render are optimized by the browser, and if it considers that there is no need to redraw anything in this cycle, then Event Loop will simply go further.
After Render, the cycle repeats again, and so on until the browser tab closes and the process ends.
If one of the customers did not have tasks, then Event Loop just goes to the next. And, on the contrary, if the customer's tasks take a lot of time, then the remaining customers will wait for their turn. And if the tasks from some customer turned out to be endless, then Call Stack overflows, and the browser starts to swear:
Figure 3 Now that we understand how Event Loop works, it's time to figure out what happens after the code snippets are executed at the beginning of this article.
function foo() { setTimeout(foo, 0); } foo();
We see that the function foo calls itself recursively through setTimeout inside, but with each call it creates a Tasks customer task. As we recall, in the Event Loop loop, when executing the task queue from Tasks, it takes only 1 task in the loop. And then there are tasks from Microtasks and Render. Therefore, this piece of code will not cause Event Loop to suffer and forever rake its tasks. But it will throw up a new task for the customer Tasks on each lap.
Let's try to run 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 on the 'start profiling and reload page' button:
Figure 4We see that the tasks from Tasks are performed one at a time, about once every 4ms.
Consider the second puzzle:
function foo() { Promise.resolve().then(foo); } foo();
Here we see the same thing as in the example above, but calling foo adds tasks from Microtasks, and they are all done until they end. And this means that until Event Loop finishes them, he will not be able to move on to the next customer :( And we again see a sad
picture .
Take a look at this in the development tools:
Figure 5We see that microtasks are executed approximately once every 0.1ms, and this is 40 times faster than the Tasks queue. All because they are performed all at once. In our example, the queue moves endlessly. For visualization, I reduced it to 100,000 iterations.
That's all!
I hope this article was useful to you, and now you understand how Event Loop works and what’s going on in the code examples above.
Bye :) And see you soon. If you liked it, like and subscribe to my channel :)