This paper describes the execution process of js running mechanism on the browser side and node side, and compares the running mechanism of the two. It also explains synchronous task and asynchronous task, the necessity of the two asynchronous tasks, and the callbacks and the priority of some callbacks.

JS runtime mechanism retelling

First of all, there will be a stack, a task queue, a microtask queue, and an event loop.

  • Main thread: function execution stack used to store synchronization tasks, executed in last in, first out order;
  • In the task queue, macro tasks are stored.
  • When the function execution stack is empty, the event loop mechanism will be started, and the task queue will be executed in the execution stack. Before that, when a task is fetched from the task queue, if there is a task in the microtask queue, the microtask is executed first and the task in the task queue is executed.
  • Loop until the task queue, microtask queue, and function execution stack are empty.

The attached:

Synchronous task: A task that is executed on the main thread and can only be executed after the previous task has completed. Asynchronous tasks: do not enter the main thread execution, but enter the task queue execution.

Event loop in Node.js

The libuv library is responsible for executing the Node API, assigning it to different threads and creating an event loop.

The event loop in node is roughly divided into six parts.

  • Timer Timer: performs the callback of setTimeout and setInterval.
  • I/O callback: handles network, stream, and TCP error callbacks
  • Idle and PREPARE phases: Used within the node
  • Poll polling: Runs the I/O queue in the poll to check whether the timer is available
  • Check: Stores the setImmediate callback
  • Close callback: The close callback

The main components are timer, poll, and check. We are only here to understand.

Event loop process:

  • Synchronous code that executes global Script.
  • After the synchronization code call stack is cleared, the microtask is executed. Execute all tasks in the NextTick Queue and then all tasks in other microtask queues.
  • Start executing the macro task, the above six stages. Starting with phase 1, execute all tasks in the macro task queue corresponding to each phase. (After the execution of the macro task queue in each phase is completed, the microtask is executed), and then the macro task in the next phase is started, which in turn forms an event loop.
  • Timers Queue -> execute microtasks -> I/O Queue -> Execute microtasks -> Check Queue -> execute microtasks -> Close Callback Queue -> Execute microtasks…

The difference between browser and Node side event loops

  • The two mechanisms are completely different, and the implementation mechanism is also different.
  • Node.js can be understood as four macro task queues (Timer, I/O, check, close) and two microtask queues. But there are six phases to executing a macro task.
  • When node.js starts the 6 phases of macro task, all tasks in the macro task queue will be taken out for execution in each phase. After the execution of macro task in each phase, microtasks will be executed. However, the event loop in the browser is to only fetch a macro task for execution, and then see whether the microtask queue exists, and then fetch a macro task to form a loop.

JS Asynchronous tasks

Js asynchronous tasks are divided into two types: macro tasks and micro tasks. A macro task can have multiple microtasks, and the internal microtasks will only be executed when the JS code block is executed.

Macro task

Macrotask, also called tasks. Some asynchronous task callbacks will in turn enter the macro task queue, waiting for the subsequent back call.

Macro tasks include:

  • setTimeout/setInterval
  • SetImmediate (Node exclusive)
  • RequestAnimationFrame (browser only)
  • I/O
  • UI Rendering (browser only)

Note: 1, setTimeout has a delay of 0, and is compared to requestAnimationFrame: requestAnimationFrame has a higher priority than setTimeout.

2, setTimeout latency is 0, compared with setImmediate: uncertain.

setTimeout(() = > console.log('setTimeout'), 0)
setImmediate(() = >{
  console.log('setImmediate');
})
Copy the code

Description: If the preparation time before the timer is more than 1ms (the time from loop to timer is more than 1ms), the callback function of the timer phase (setTimeout) is executed. If the preparation time before the timer is less than 1ms, the callback function in the check phase (setTimeout) is executed first, and then the callback function in the timer phase is executed after the next event loop.

If you want to setImmediate to perform first, you can use fs file packages to ensure execution during the I/O callback phase. So in this time loop, the chack phase is executed, and then the Timer phase is executed.

SetTimeout in the node version

setTimeout(() = > {
  console.log(1)})setTimeout(() = > {
  console.log(2)
  Promise.resolve().then(function () {
    console.log('promise')})})setTimeout(() = > {
  console.log(3)})Copy the code
  • After Node11, the browser runs the same: 1, 2, PROMISE 3.
  • For versions earlier than node11, the execution result is 1, 2, and 3 promise. It will first enter the Timer phase, execute the first setTimeout and print. The second setTimeout is executed and printed, and the Promise is placed in the microtask queue. Then execute a third setTimeout and print. The event loop executes the microtask queue and prints the promise before executing the next phase.

Micro tasks

Microtask, also called Jobs. Asynchronous callbacks other than macro tasks are queued for microtasks to be called later. Microtasks include:

  • Process. nextTick (unique to Node)
  • Promise.then()
  • Object.observe
  • MutationObserve

Note: Process.nexttick has higher priority than promise.then ().

The need for two asynchronous tasks

In the asynchronous task queue, the first in, first out principle is followed. In this case, among many asynchronous tasks, if there are tasks with higher priority that need to be executed first, only one asynchronous task queue cannot be satisfied. In this case, it is necessary to introduce a microtask queue and put the tasks with higher priority into the microtask queue. If the microtask queue is not empty, the microtask queue is executed, otherwise the macro task queue is executed. If there is only one type of asynchronous task, the asynchronous task with the highest priority cannot be executed first.

supplement

async/await

Async /await is essentially a wrapper around promises

Async function code before the await is executed synchronously, which can be understood as code passed in when the code before the await is part of a new Promise, and all code after the await is a callback in promise.then.