Js runtime mechanism

Event Loop Event loop

  1. Js is divided into synchronous tasks and asynchronous tasks, all synchronous tasks are executed on the main thread
  2. There is also a “task queue” in which an event is added whenever an asynchronous task has a result
  3. When all synchronization tasks on the main thread are finished and the execution stack is clear, the task queue is read and the tasks are successively thrown onto the main thread
  4. This process continues to loop, becoming the JS event loop mechanism.

So it’s not hard to understand that sometimes setTimeout(fn, 0) is not executed immediately, it’s just added to the task queue immediately, maybe the main thread hasn’t finished at that time, so it has to wait until the JS engine is idle to execute.

The task queue is controlled by the js event-triggered thread, not by the JS engine. Perhaps the JS engine is too busy, and the browser opens a separate thread

Macrotask, Microtask

If we come across something like this, who’s going to execute first, which brings us to the concept of macro tasks, microtasks. In addition to synchronous tasks, asynchronous tasks are divided into macro tasks and micro tasks.

console.log(1)

Promise.resolve().then(() => {
    console.log('promise')
})

setTimeout(() => {
    console.log('settimeout')  
})
Copy the code
Macro task

A script (the overall code) is a macro task (js is an interpreted language, it first generates the AST syntax tree, stored in memory, and then executes while interpreting). This includes adding events from the task queue to the execution of the main thread. A macro task is executed as a task, and the browser rerenders the page after one macro task finishes but before another one begins.

Common MacroTask: setTimeout, setInterval, setImmediate, I/O, and so on

Micro tasks

What is MicroTask? What happens immediately after a task executes, that is, a macro task executes, and then it is generated during execution until all microtasks are executed, that is, the queue of microtasks is cleared, and the browser starts to rerender the page.

Common microtasks: promise. then, process. NextTick, MutaionObserver

summary

Combined with macro tasks, micro tasks to summarize the JS event loop mechanism.

  1. Starts execution of a macro task (the entire script code is pushed as the first macro task and starts execution).
  2. In the case of asynchron, wait for the results of the asynchronous tasks before putting them in the event queue, or in the microtask queue if they are microtasks.
  3. The current macro task is completed, and the microtasks generated during its execution are executed in turn.
  4. All microtasks are completed, and the browser starts to re-render.
  5. Go to the event queue to read the next macro task, and loop through the above process.
// For example 🌰 console.log('1'); setTimeout(function() { console.log('2'); Promise.resolve().then(function() { console.log('3'); }) new Promise(function(resolve) { console.log('4'); resolve(); }).then(function() { console.log('5') }) }, 100) Promise.resolve().then(function() { console.log('6'); }) new Promise(function(resolve) { console.log('7'); resolve(); }).then(function() { console.log('8') }) setTimeout(function() { console.log('9'); Promise.resolve().then(function() { console.log('10'); }) new Promise(function(resolve) { console.log('11'); resolve(); }).then(function() { console.log('12') }) }) console.log('13')Copy the code

For the first event loop, we first print 1, and then we encounter setTimeout, which we call setA. SetA queues outside, and then Promise.then belongs to the microtask and joins the microtask queue. If you encounter a new Promise, print 7 directly, and then join the microtask queue. And then we get setTimeout again, which we’ll call setB, and wait in line. Then print 13. So at the end of the first round of the event loop, we print 1,7,13 directly, followed by 6,8 from the microtask queue.

Now that the first round of the event loop is over, we still have setA and setB waiting to be executed. Which one should we execute first? The answer is setB. Although the timer line catches setA first, setA has a delay of 100ms, while setB is executed immediately and is added to the event queue before setA. So when the first round of event loop ends, setB is pulled from the event queue and executed on the execution stack, and the second round of event loop begins.

The second loop first prints 9, then encounters the microtask, adds 10 to the microtask queue, and then encounters the new Promise, directly executes 11, then the function 12 is added to the microtask, the end of the event loop. We printed 9,11,10,12.

The third round of the event loop begins. The macro task in which setA is located first prints 2, and then 3 is added to the microtask. Similarly, prints 4, and then 5 of the function is added to the microtask queue. The event cycle ends, printing 2,4,3,5.

Final results: 1,7,13,6,8,9,11,10,12,2,4,3,5

Reference: dailc.github. IO