In the browser, there are two task queues, one for macro tasks and one for micro tasks. However, there are six event queues in NodeJS: Timers, Pending Callbacks, Idle prepare, Poll, Check, and Close Callbacks. Each queue holds the callback function.

The six queues are executed in sequence. Each queue is responsible for storing different tasks.

The callback functions of setTimeout and setInterval exist in timer

Pending callbacks are operating system callbacks, such as TCP or UDP.

Idle and prepare are used only in the system. The average developer doesn’t use it

Poll performs iO-related callback operations

Check stores the callback in setImmediate.

Close Callbacks Perform a callback for the close event.

In Node, the code is executed synchronously from top to bottom. During the execution, different tasks are added to corresponding queues. For example, setTimeout is stored in timers; in case of file reading and writing, it is stored in poll. Imagine a queue for microtasks that has nothing to do with the previous six.

When the synchronization code is completed, the microtasks that meet the conditions will be executed. Once all the microtasks are completed, the macro tasks that meet the conditions in the queue will be executed in the order listed above.

First, it executes macro tasks that meet the conditions in Timers. After completing tasks satisfied in Timers, it performs queue switching and empties micro-tasks in the micro-task list before switching.

Therefore, there are two timing for microtask execution. The first time is when all synchronization codes are executed, and the second time is before queue switchover.

Note that in microtasks nextTick takes precedence over Promise, which is just a memorization.

setTimeout(() = > {
    console.log('s1');
})

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

console.log('start');

process.nextTick(() = > {
    console.log('tick');
})

setImmediate(() = > {
    console.log('st');
})

console.log('end');

// start end tick p1 s1 st
Copy the code
setTimeout(() = > {
    console.log('s1');
    Promise.resolve().then(() = > {
        console.log('p1');
    })
    process.nextTick(() = > {
        console.log('t1'); })})Promise.resolve().then(() = > {
    console.log('p2')})console.log('start');

setTimeout(() = > {
    console.log('s2');
    Promise.resolve().then(() = > {
        console.log('p3');
    })
    process.nextTick(() = > {
        console.log('t2'); })})console.log('end');

// start end p2 s1 s2 t1 t2 p1 p3
Copy the code

Node and browser event loop execution are a little different.

First of all, the number of task queues is different. Generally, browsers only have two queues: macro task and micro task, while Node has six event queues in addition to the micro task queue.

Second, the timing of microtask execution is different, but they also have the same thing: after the completion of synchronous task execution, they all go to check whether the microtask can be executed. The browser emptying the microtask queue every time a macro task completes. The microtask queue is emptied in Node only when the event queue is switched.

Finally, microtask execution is prioritized on The Node platform, with nextTick taking precedence over Promise.then, and first-in, first-out in the browser.

setTimeout(() = > {
    console.log('timeout');
})

setImmediate(() = > {
    console.log('immdieate');
})
Copy the code

This is because setTimeout is required to receive a time parameter. If it is not written, it will be a 0. We all know that in Node or in the browser, the program can not really be 0. It depends on the running environment.

If setTimeout is executed first, it is placed in the timers queue so timeout is entered first, and if setTimeout is executed later for some reason, the IMMdieate in the Check queue is executed first. That’s why it prints timeout and immdieate.

const fs = require('fs');

fs.readFile('./a.txt'.() = > {
    setTimeout(() = > {
        console.log('timeout');
    }, 0)

    setImmediate(() = > {
        console.log('immdieate'); })})Copy the code

In this case, immdieate is always printed first and timeout is always printed after timeout. This is because timeout is added to timers, fs callback is added to poll, and Immdieate is added to check. After the fs callback is completed, when the poll queue is switched, the micro tasks will be looked at first. However, if there are no micro tasks in the poll queue, the micro tasks will continue downwards. The check queue instead of timers queue is below, so the IMMdieate callback will be switched to the Check queue after poll empties.