Recently, I became interested in Event loop, so I learned about it. However, I found that although there are many articles in the whole Event loop, none of them can understand all its contents after reading them. Most articles are about either browser or Node, so if you don’t have a comparison, it’s always a bit shallow. Hence this long article.

Definition 1.

Event loop: an Event loop is a JavaScript engine
Processing asynchronous TasksThe way. In order for single-threaded JavaScript to run smoothly, all asynchronous operations need to be handled properly. This processing logic is called the Event loop.

As mentioned in a previous article, a JavaScript engine, also known as a JavaScript interpreter, is a tool that interprets JavaScript into machine code and runs in a browser and Node, respectively. The Event loop has different implementations depending on the context: Node uses the Libuv library to implement the Event loop; In browsers, the HTML specification defines Event loops, which are implemented by different vendors.

Therefore, browser Event loop and Node Event loop are two different concepts.

Meaning of 2.

In practice, knowing what Event loops mean can help you analyze some of the problems with asynchronous ordering (of course, with the popularity of ES7 async and await, there are fewer and fewer of these opportunities). In addition to this, it has a positive effect on your understanding of the internal mechanics of the browser and Node; You won’t be blindfolded when asked about the sequence of asynchronous operations in an interview.

3. Implementation on the browser

In JavaScript, tasks are divided into tasks (also called macrotasks) and microtasks (microtasks). They contain the following contents respectively:

MacroTask: Script, setTimeout, setInterval, setImmediate (node only), I/O, UI rendering



MicroTask: Process. nextTick (node exclusive), Promises, Object.observe(deprecated), MutationObserver

One thing to note is that the overall order of execution in the same context is synchronous code – >microTask – >macroTask[6]. We’ll talk about that later.

In browsers, an event loop is made up of many task queues, which are shared by different task sources. The tasks in each queue are executed strictly in first-in, first-out order. However, the execution order of tasks in different task queues is uncertain because of the browser’s own scheduling.

To be specific, the browser will continuously fetch tasks from the task queue and execute them sequentially. After each task is completed, the browser will check whether the MicroTask queue is empty (the function stack is empty when the execution of a task is complete). If not, the browser will execute all microtasks at once. It then enters the next loop to fetch the next task from the task queue, and so on.

Note: The orange MacroTask queue should also be constantly being switched.

What is an Event Loop in a browser?

4. Implementation on Node

Nodejs event loop is divided into six stages, which are repeated in sequence as follows:

  1. Timers: Execute the callback that expires in setTimeout() and setInterval().
  2. I/O Callbacks: A small number of I/ OCallbacks in the previous cycle are delayed until this stage of the cycle
  3. Idle, prepare: Moves the queue. It is used internally only
  4. Poll: The most important phase, performing I/O callback, will block in this phase under appropriate conditions
  5. Check: Performs the Callback of setImmediate
  6. Close callbacks: Callback to execute a close event, such as socket.on(“close”,func)

Unlike the browser, the microTask queue is executed after each phase, rather than after the MacroTask task is complete. This causes the same code to behave differently in different contexts. We will explore this below.

Note also that setImmediate is used in the Check phase of the loop if created during the Timers phase, and setTimeout is used in the timers phase to enter the next loop because timers have been removed. The same is true for creating timers during the Check phase.

Example 5.

5.1 Difference between Browser and Node Execution Sequence

setTimeout(()=>{ console.log('timer1') Promise.resolve().then(function() { console.log('promise1') }) }, Console.log ('timer2') promise.resolve ().then(function() {console.log('promise2')})}, 0) Time1 promise1 time2 promise2 Node output: time1 time2 promise1 promise2Copy the code

In this example, Node’s logic is as follows:

Timer1 and Timer2 are initially in the Timers stage. The timers stage is started first, execute the callback function of Timer1, print timer1, and place the promise1. Then callback into the MicroTask queue. Repeat the same steps to execute timer2 and print Timer2. Before the Event loop enters the next phase, all tasks in the MicroTask queue are executed and promisE1 and promisE2 are printed successively.

Browsers, with two setTimeouts as two MacroTasks, output Timer1, promise1 and timer2, promise2.

Further information can be found in understanding JS Event Loops in Depth (Node.js).

To prove our theory, change the code to look like this:

setImmediate(() => { console.log('timer1') Promise.resolve().then(function () { console.log('promise1') }) }) Resolve (). Then (function () {console.log('promise2')})}, 0) Node Timer1 TIMer2 promisE1 or timer2 Timer1 promisE2 PromisE1Copy the code

SetTimeout (fn,0) should be faster than setImmediate(FN). There should only be a second result. This is because Node can’t do 0 milliseconds, or at least 1. When you actually execute, once you get into the event loop, it might be 1 millisecond, or it might not be 1 millisecond, depending on what the system is doing. If less than 1 millisecond is reached, the Timers phase is skipped and proceeds to the Check phase, where the setImmediate callback function is first executed.

Also, setImmediate is faster than setTimeout if the Timer phase has passed, for example:

const fs = require('fs');

fs.readFile('test.js', () => {
  setTimeout(() => console.log(1));
  setImmediate(() => console.log(2));
});Copy the code

The above code enters the I/O callbacks phase, then the Check phase, and finally the Timers phase. Therefore, setImmediate executes before setTimeout.

For details, see Node Timer.

5.2 Execution speed of Asynchronous Tasks

setTimeout(() => console.log(1)); setImmediate(() => console.log(2)); Promise.resolve().then(() => console.log(3)); process.nextTick(() => console.log(4)); Output: 4, 3, 1, 2 or 4, 3, 2, 1Copy the code

Since we said that microTask will run better than macroTask, output the following two first, and process.nextTick takes precedence over Promise in Node [3], so 4 comes before 3. According to what we said before, Node has no absolute 0ms, so the order of 1,2 is not fixed.

5.3 MicroTask queue and MacroTask Queue

setTimeout(function () { console.log(1); }, 0); console.log(2); process.nextTick(() => { console.log(3); }); new Promise(function (resolve, rejected) { console.log(4); resolve() }).then(res=>{ console.log(5); }) setImmediate(function () { console.log(6) }) console.log('end'); Node output: 2 4 end 3 5 1 6Copy the code

This example comes from “Execution Mechanisms in JavaScript.” The Promise code is synchronous, and then and catch are asynchronous, so 4 is output synchronously. Then the Promise’s then is in microTask, superior to other tasks in the macroTask queue, so 5 is output better than 1,6. And the Timer is better than the Check phase, so 1,6.

6. Summary

To sum up, there are a few rules for the most critical order:

  1. MicroTask will run before MacroTask in the same context
  2. The browser then runs a MacroTask task in order of all microTasks, and Node runs in order of six phases, with MicroTask queues running after each phase
  3. In the same MicroTask queueprocess.tick()Will be better thanPromise

The Event loop is relatively profound, and there will be a lot of interesting things if you go deep into it. Please feel free to point out any problems.

Reference Documents:

  1. What is an Event Loop in a Browser?
  2. Don’t Confuse NodeJS with Event Loops in browsers
  3. Node Timer Details
  4. Event Loops for Browsers and Nodes
  5. Deep Understanding of JS Event Loops (Node.js)
  6. “Execution Mechanisms in JavaScript”