Introduction to the

In the last article we briefly introduced the event and event loop in NodeJS. In this article, we’ll take a closer look at nodeJS events and explore the differences between setTimeout, setImmediate, and Process.nexttick.

Event loops in NodeJS

Although NodeJS is single-threaded, nodeJS can delegate operations to the system kernel, which handles these tasks in the background and notifies NodeJS when the task is complete, triggering the callback method in NodeJS.

These callbacks are added to the round robin queue and eventually executed.

With this event loop design, NodeJS can eventually implement non-blocking IO.

The Event loop in NodeJS is divided into phases. The following figure lists the execution order of each phase:

Each phase maintains a callback queue, which is a FIFO queue.

When entering a phase, tasks of the phase will be performed first, and then callback tasks belonging to the phase will be performed.

When all the tasks in the callback queue have been executed or the maximum number of callback executions has been reached, the next phase is entered.

Note that Windows and Linux implementations are slightly different, and we’ll focus on the most important phases here.

Q: Why limit the maximum number of callback executions during phase execution?

A: In extreme cases, a phase may need to execute a large number of callbacks. If it takes too long to execute these callbacks, nodeJS will be blocked, so we set a limit on the number of callback executions to avoid long nodeJS blocks.

Phase,

In the figure above, we have listed six phases, which will be explained one by one.

timers

Timers the Chinese word for timers is used to run a callback function at a given time or interval.

There are two common timers functions: setTimeout and setInterval.

Typically these callback functions are executed as soon as possible after expiration, but are affected by other callback executions. Let’s look at an example:

const fs = require('fs');

function someAsyncOperation(callback) {
  // Assume this takes 95ms to complete
  fs.readFile('/path/to/file', callback);
}

const timeoutScheduled = Date.now();

setTimeout(() = > {
  const delay = Date.now() - timeoutScheduled;

  console.log(`${delay}ms have passed since I was scheduled`);
}, 100);

// do someAsyncOperation which takes 95 ms to complete
someAsyncOperation(() = > {
  const startCallback = Date.now();

  // do something that will take 10ms...
  while (Date.now() - startCallback < 10) {
    // do nothing}});Copy the code

In the example above, we call someAsyncOperation, which first goes back to the readFile method, assuming it takes 95ms. The callback function of readFile is then executed, which will run for 10ms. Finally go back and execute the callback in setTimeout.

So in the above example, although setTimeout is specified to run after 100ms, it will actually wait until 95 + 10 = 105 ms before it actually executes.

pending callbacks

This phase will perform some system callback operations, such as ECONNREFUSED when TCP connections are made to the TCP socket. This error will be reported on some liunx operating systems. The system’s callback will run in pending Callbacks.

Or the I/O callback operation that needs to be performed in the next Event loop.

idle, prepare

Idle and prepare are phases for internal use, which will not be described here.

Poll polling

Poll will detect new I/O events and perform I/ O-related callbacks, noting that callbacks here refer to almost all callback events except for turning off callback, Timers, and setImmediate.

Poll does two main things: polling I/O, timing blocks, and processing events in the Poll queue.

If the poll queue is not empty, the Event loop will traverse the callbacks in the queue and execute them synchronously one by one until the queue is consumed or the number of callbacks reaches its limit.

Because the callbacks in the queue are executed synchronously one by one, they can block.

If the Poll queue is empty and setImmediate is called in the code, then the callback in setImmediate will immediately jump to the next Check phase. If setImmediate is not called, it continues to wait for the new callback to be added to the queue and executed.

check

Primarily to perform the setImmediate callback.

SetImmediate setImmediate can be thought of as a unique timer that runs in a separate phase and uses the Underlying Libuv API to plan callbacks.

In general, if a callback in a poll phase is called through setImmediate, the Poll phase is not clear and the Check phase executes the corresponding callback.

close callbacks

The final phase deals with callbacks in close events. For example, if a socket is suddenly closed, a close event is raised and the associated callback is called.

The difference between setTimeout and setImmediate

What’s the difference between setTimeout and setImmediate?

As you can see from the phase phase above, the Callback in setTimeout is performed in the Timer phase, while setImmediate is performed in the Check phase.

Semantically, setTimeout means that a callback is run after a given time. SetImmediate, on the other hand, executes immediately after performing the I/O in the loop.

So what’s the difference in order of execution between these two methods?

Here are two examples. In the first example, both methods run in the main module:

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

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

The order of execution of the two methods thus run is uncertain because it can be influenced by other executants.

The second example is to run both methods in an I/O module:

const fs = require('fs');

fs.readFile(__filename, () = > {
  setTimeout(() = > {
    console.log('timeout');
  }, 0);
  setImmediate(() = > {
    console.log('immediate');
  });
});
Copy the code

You’ll notice that in an I/O module, setImmediate will definitely perform before setTimeout.

The similarities between the two

SetTimeout and setImmediate both have a return value that allows the Timer to clear:

const timeoutObj = setTimeout(() = > {
  console.log('timeout beyond time');
}, 1500);

const immediateObj = setImmediate(() = > {
  console.log('immediately executing immediate');
});

const intervalObj = setInterval(() = > {
  console.log('interviewing the interval');
}, 500);

clearTimeout(timeoutObj);
clearImmediate(immediateObj);
clearInterval(intervalObj);
Copy the code

The clear operation can also clear intervalObj.

Ref and unref

SetTimeout and setInterval both return Timeout objects.

If the timeout object is the last one to execute, you can cancel it using the unref method, and after canceling it, you can resume it using ref.

const timerObj = setTimeout(() = > {
  console.log('will i run? ');
});

timerObj.unref();

setImmediate(() = > {
  timerObj.ref();
});
Copy the code

Note that if there are multiple timeout objects, only the unref method of the last timeout object will take effect.

process.nextTick

Process. nextTick is also an asynchronous API, but it is different from timer.

If we call process.nextTick in a phase, the callback in nextTick will complete before this phase completes and the event loop enters the next phase.

One problem with this is that if we make recursive calls in process.nextTick, the phase will block, affecting the event loop execution.

So why do we still have Process.nexttick?

Consider the following example:

let bar;

function someAsyncApiCall(callback) { callback(); }

someAsyncApiCall(() = > {
  console.log('bar', bar); // undefined
});

bar = 1;
Copy the code

In the example above, we define a someAsyncApiCall method that executes the callback function passed in.

This callback wants to print the value of bar, but the value of bar is assigned after the someAsyncApiCall method.

This example will eventually result in the output bar value being undefined.

We want the user to call callback after the program has finished executing, so we can rewrite the above example with process.nextTick:

let bar;

function someAsyncApiCall(callback) {
  process.nextTick(callback);
}

someAsyncApiCall(() = > {
  console.log('bar', bar); / / 1
});

bar = 1;
Copy the code

Let’s look at another example in action:

const server = net.createServer(() = > {}).listen(8080);

server.on('listening'.() = > {});
Copy the code

The example above is the simplest nodeJS creation of a Web service.

What’s wrong with the above example? The Listen (8000) method will immediately bind port 8000. However, at this point, the server’s Listening event binding code has not been executed.

This actually uses the process.nextTick technology so that we can listen for listening events wherever we bind them.

The difference between Process. nextTick and setImmediate

Process. nextTick executes the callback immediately in the current phase, while setImmediate executes the callback in the Check phase.

So Process. nextTick takes precedence over setImmediate.

In fact, the semantics of Process. nextTick and setImmediate should be interchangeable. Process. nextTick indicates immediate, and setImmediate indicates the next tick.

Author: Flydean program stuff

Link to this article: www.flydean.com/nodejs-even…

Source: Flydean’s blog

Welcome to pay attention to my public number: “procedures those things” the most popular interpretation, the most profound dry goods, the most concise tutorial, many you do not know the small skills you find!