Thoroughly understand browser Event-loop

  Front end, javascript, node.js, Programmer

Why did you write this blog post?

Some time ago, I chatted with a small friend of the headline asking what questions the front end of the interview would ask. He said that if it was his interview, event-loop would definitely ask. I talked a lot that day, and event-loop left a deep impression on me. the reason is very simple, because I have never known it in depth before. if I met this question during the interview, the answer was definitely not satisfactory.

Therefore, I recently read some related articles, combed them carefully and output this blog post, hoping to help you understand the browser’s event-loop. The event-loop in node will be supplemented later.

1. Preparatory knowledge

JavaScript’s Operating Mechanism:

(1) All synchronization tasks are executed on the main thread to form an execution context stack.

(2) Besides the main line, there is also a “task queue”. As long as asynchronous tasks have run results, an event is placed in the “task queue.”

(3) Once all synchronous tasks in the “execution stack” are completed, the system will read the “task queue” to see what events are in it. The corresponding asynchronous tasks end the waiting state, enter the execution stack, and begin execution.

(4) The main thread repeats the above third step continuously

The summary is as follows: when the synchronous tasks in the call stack have been executed and the stack has been emptied, the main thread is idle. at this time, the main thread will go to the task queue to read one task in sequence and put it into the stack for execution. Every time the stack is emptied, it will read if there are any tasks in the task queue. if there are any, it will read and execute, and it will read and execute in a loop all the time.

There is one or more task queues in an event cycle

There are two asynchronous tasks in JavaScript:

  1. Macro task: script (whole code), settimeout, setinterval, setimmediate, i/o, ui rendering
  2. Microtasks: process.nexttick (nodejs), promotions, object.observe, mutationobserver;

2. What is the event-loop?

The main thread reads the execution events from the “task queue”. This process is continuous and the mechanism is called event loop. The mechanism is as follows: the main thread will continuously fetch tasks from the task queue for execution in sequence, and will check whether the microtask queue is empty every time a task is executed (the specific flag for executing a task is that the function execution stack is empty), and if it is not empty, all microtasks will be executed at one time. Then enter the next cycle to execute the next task in the task queue.

Details:

  1. Select the macro task queue to be executed currently, and select the macro task that enters the task queue first. If there is no macro task to select, it will jump to the execution step of microtask.
  2. Sets the currently running macro task of the event loop to the selected macro task.
  3. Run macro tasks.
  4. Set the currently running task of the event loop to null.
  5. Remove the completed macro task from the macro task queue.
  6. Microtasks step: enter the microtask checkpoint.
  7. Update interface rendering.
  8. Return to the first step.

The specific steps for entering the microtask check are as follows:

  1. Set the flag to true for entering the microtask checkpoint.
  2. When the microtask queue of the event cycle is not empty: select a microtask that enters the microtask queue first; Set the currently running task of the event loop as the selected microtask;; Run microtask;; Set the current running task of the event loop to null;; Remove the completed microtask from the microtask queue.
  3. For each environment settings object of the corresponding event loop, inform them which promise is rejected.
  4. Clean up transactions in indexedDB.
  5. Set the flag to false when entering the microtask checkpoint.

It should be noted that when the current execution stack finishes executing, it will immediately process all the events in the micro task queue, and then fetch an event from the macro task queue. In the same event cycle, micro tasks are always executed before macro tasks.

Figure:

clipboard.png

How does event-loop work?

Let’s look at a simple example:

setTimeout(()=>{
    console.log("setTimeout1");
    Promise.resolve().then(data => {
        console.log(222);
    });
});
setTimeout(()=>{
    console.log("setTimeout2");
});
Promise.resolve().then(data=>{
    console.log(111);
});

Think about it, what is the result of the operation?

The results of the operation are as follows:

111
setTimeout1
222
setTimeout2

Let’s take a look at why.

Let’s explain in detail how the JS engine executes this code:

  1. There is no code to execute on the main thread
  2. Then we encounter setTimeout 0, which is used to put the callback function into the macro task queue after 0ms (this task will be executed in the next event cycle).
  3. Then we encounter setTimeout 0, which is used to put the callback function into the macro task queue after 0ms (this task will be executed in the next event cycle).
  4. First, check the microtask queue, i.e. microtask queue, and find that the queue is not empty. then execute the first promise callback and output’ 111′.
  5. At this time, the microtask queue is empty, enter the next event cycle, check the macro task queue, find the callback function with setTimeout, immediately execute the callback function output’ setTimeout1′, check the microtask queue, find the queue is not empty, execute then callback of promise, output’ 222′, microtask queue is empty, enter the next event cycle.
  6. Check the macro task queue, find a callback function with setTimeout, and immediately execute the callback function output’ setTimeout2′.

Think again about the execution sequence of the following code:

console.log('script start');

setTimeout(function () {
    console.log('setTimeout---0');
}, 0);

setTimeout(function () {
    console.log('setTimeout---200');
    setTimeout(function () {
        console.log('inner-setTimeout---0');
    });
    Promise.resolve().then(function () {
        console.log('promise5');
    });
}, 200);

Promise.resolve().then(function () {
    console.log('promise1');
}).then(function () {
    console.log('promise2');
});
Promise.resolve().then(function () {
    console.log('promise3');
});
console.log('script end');

Think about it, what is the result of the operation?

The results of the operation are as follows:

script start
script end
promise1
promise3
promise2
setTimeout---0
setTimeout---200
promise5
inner-setTimeout---0

Then why?

Let’s explain in detail how the JS engine executes this code:

  1. First, the synchronization tasks on the main process, console.log of the first sentence and the last sentence are executed sequentially
  2. Then we encounter setTimeout 0, which is used to put the callback function into the macro task queue after 0ms (this task will be executed in the next event cycle).
  3. Next, we encounter setTimeout 200, whose function is to put the callback function into the macro task queue after 200ms (this task will be executed in the next event cycle).
  4. After the synchronous task is executed, first check the microtask queue, i.e. microtask queue, and find that the queue is not empty. then execute the then callback of the first promise, output’ promise1′, and then execute the then callback of the second promise, output’ promise3′. since the return of. then () of the first promise is still promise, the second. then () will be put into the micro task queue to continue execution and output’ promise2′;
  5. At this time, the microtask queue is empty, enter the next event cycle, check the macro task queue, find out the callback function with setTimeout, immediately execute the callback function output’ setTimeout—0′, check the microtask queue, the queue is empty, enter the next event cycle.
  6. Check the macro task queue and find a callback function with setTimeout. Immediately execute the callback function output’ setTimeout—200′.
  7. Then encounter setTimeout 0, its function is to put the callback function into the macro task queue after 0ms, check the microtask queue, i.e. micro task queue, find that the queue is not empty, execute then callback of promise, and output’ promise5′.
  8. At this time, the microtask queue is empty, enter the next event cycle, check the macro task queue, find the callback function with setTimeout, immediately execute the callback function output, and output’ inner-setTimeout—0 0′. the code execution ends.

4. why do you need event-loop?

Because JavaScript is single threaded. Single thread means that all tasks need to queue up and the last task will not be executed until the last one is finished. If the previous task takes a long time, the latter task has to wait all the time. In order to coordinate events, user interaction, script, rendering, networking, etc., the user agent must use event loops.

5. Reference articles:

  1. https://segmentfault.com/a/11 …
  2. https://segmentfault.com/a/11 …
  3. https://segmentfault.com/a/11 …
  4. http://www.ruanyifeng.com/blo …

Thank you for taking the precious time to read this article. If this article gives you some help or inspiration, then don’t be stingy with your praise and Star Ha. Your affirmation is my greatest motivation to move forward.https://github.com/YvetteLau/ …

Recommend to pay attention to my public number:

clipboard.png