[Nodejs principle source & Appreciation (7)] [translation] in the event loop Node.js, timers and process.nextTick

Abstract official website Bo translation, nodejs the timerspacer.gif

Sample code is hosted: http://www.github.com/dashnowords/blogs

Original Address: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick

If you walk in the perennial Nodejs Chinese network , may have missed first-hand information on the official website, Nodejs Chinese network did not translate very high quality core articles that provide only a Chinese version of the API documentation (already not easy , without any black it means, I am also a beneficiary of Chinese network), which covers the core concepts from Node.js tools, etc. related to very important knowledge, the following is a blog directory, you know how to do it .

spacer.gifpic1.PNG

What Event Loop that?

Event loop is the Node.jsbasis to achieve non-blocking I / O, although JavaScript is single-threaded applications running, but it can be passed down to the operating system kernel to execute.

Most modern system kernel is support for multithreading, which can handle multiple operations at the same time in the background. When any one task is completed, the kernel will be notified Node.jsso that it can be added to the corresponding callback poll queue, the callback function can be executed eventually, later we will conduct a more detailed explanation.

Event Loop basic explanation

When Node.jsstart running, it will be initialized Event Loop, and then deal with the script file (or in REPL( the Read-eval-Print-Loop execution) environment, the article does not discuss in depth) asynchronous API calls, timer, or a process.nextTickmethod call, then it will begin processing the event loop (event loop).

The following diagram shows the various stages of the event cycle (each box is called an event loop in a "stage"):

spacer.gifpic2.PNG

Each stage maintains a callback function to be executed a FIFO queue, even though each stage has its own unique approach, but overall, when entering the event loop a specific phase, it will deal with this stage For all operations, and then execute this phase corresponds to the callback queue until the queue is empty, or up to the maximum number of stages allowed to run the function, when any one condition is met, the event loop to the next stage will be to forth.

Because any stage related operations are likely to lead to more operations to be performed to generate, and the new event will be added to the kernel poll the queue, when the poll allowed in the queue callback function is executed to continue the current phase of the poll to add a new queue callback function, so the callback function might lead to a long-running event loop in poll dwell stage for too long, you can post text timersand pollviewing more content sections.

Tip: Windows and Unix / Linux has achieved on the small differences, but does not affect the presentation of this paper, different systems may exist 7-8 stages, but the final stage to the diagram above has demonstrated, these are Node .js practical use to.

Event loop stage Overview

  • timers- performed by this stage setTimeout( )and setInterval( )plan tasks have been added to

  • pending callbacks- some of the I / O delay callback function to execute the next cycle (here not sure)

  • idle,prepare- stage internal use

  • poll- Check that the new I / O event; implementation of the relevant I / O callback (in addition to "close the callback", "Timer callback" and setImmediate( )outside callback added almost every other callback functions); node is likely to produce obstruction here

  • check- Perform setImmediate( )add callback function

  • close callbacks- for closing the callback function, e.g.socket.on('close',......)

In the period between each round of the event, Node.jsit will check whether there is waiting for asynchronous I / O or timer, and if not, they would close the current program.

Event Loop details

timers

A timer will be a definite point in time, the callback function will be performed at this point in time after time over, rather than developers want the exact time. Once the timer expires, the callback function will be scheduled for execution as early as possible, but the operating system scheduling and other callbacks are likely to lead to a timer callback function is delayed.

Tip: Technically, poll phase control how the timers to be executed.

The following example, you use a 100ms after the timer expires, then took 95ms using asynchronous file read API asynchronous read a file:

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
 }
});

 

When the event loop into the poll stage, it is to be carried queue is empty ( fs.readFile( )not yet complete), so it will wait for a certain time (the time difference between the timer expires the fastest time from the expiration of the current). After 95ms past, fs.readFile( )finished reading the file, and spent 10ms poll will be added to the execution queue callback function is that it is executed. When the callback function is finished, no more queue callback, and whether the next event loop timer will be triggered a re-examination has expired, and if so, the event will loop around back to the stage to perform timers expire the timer callback function. In this example, you will see a timer to set the timer callback function from being triggered spent a total of 105ms.

Note: In order to avoid blocking events in the poll cycle stage, libuv(C language library for implementing the underlying Node.js and asynchronous event loop characteristic) is provided with a hard upper limit (the value varies depending on the system), so that the poll stage can only be a limited number of callback function added to the poll queue.

pending callbacks

This stage will perform some callback system operations, such as some TCP errors. For example, a TCP socket object received when trying to connect to another socket ECONNREFUSED, a number of Linux systems will want to report this kind of error, such callback function will be added in phase to be executed pending callbacks queue.

poll phase

poll stage has two main functions:

  1. When calculating the length of obstruction, so that it can be completed I / O add pending queue

  2. Execution event queue poll produced

When the event is recycled into stage and at this time no poll timer to be executed, will be determined according to the following logic:

  • If you poll the queue is not empty, one by one event loop iterations to perform in a synchronized manner callback queue until the queue is depleted, or the arrival event processing system set quantity.

  • If poll the queue is empty, then the logic continues to judge according to the following:

    • If the script used setImmediate( )method to add a callback function, the event will end poll cycle stage, and check into the stage to perform these callbacks added.

    • If you do not use setimmediate( )add callback event loop will wait for other callback functions to be added to the queue and execute the function to add immediately.

Once the poll queue is empty, the event loop will check for the timers timer has expired, if there is one or more of the timer expires, the event loop will return to the stage to perform these timers timer callback function.

check

This stage allows developers to execute callbacks immediately after the poll period. If the poll stage appear idle or used in the script setImmediate( )to add a callback function, the event loop event loop will take the initiative to enter the check stage without stop and wait.

setImmediate( )It is actually a special timer runs independent phase. It is by calling the libuvAPI provides the callback function to add those want to execute at a later poll phase is complete.

Generally, as the execution of the code, the event loop will eventually reach the stage poll, it will be here waiting for incoming connection, request and other request event. However, if a callback function to be setImmediate( )poll stage is idle added, it will be the end of the stage and into the check rather than continuing to wait poll events.

close callbacks

If a socket or handle is suddenly closed (such as call socket.destroy( )), closethe event will be issued at this stage. Otherwise (otherwise trigger off) event will be by process.nextTick( )to send.

setImmediate( )和setTimeout( )

setImmediate( )And setTimeout( )it is very similar, but the performance is not the same.

  • setImmediate( )It is designed to perform some of the script after the current phase is complete poll

  • setTimeout( )We will add a script for the "tasks to be executed" after the execution of a certain time in the past

Both timer sequence is executed depends on the context of the call timer. If you are called in the main module, the timer will be related to the performance of the process (which means that it may be affecting other applications on the same machine).

For example, the following script, if we do not contain an I / O cycle of the program, their execution order because of the impact of the main thread performance can not be determined:

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

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

 

$ node timeout_vs_immediate.js
timeout
immediate

$ node timeout_vs_immediate.js
immediate
timeout

However, if the timing of these calls they placed I / O cycle, immediate callback function will be the first to be executed:

// timeout_vs_immediate.js
const fs = require('fs');

fs.readFile(__filename,()=>{
   setTimeout(()=>{
       console.log('timeout');
   },0);
   setImmediate(()=>{
       console.log('immediate');
   })
})
$ node timeout_vs_immediate.js
immediate
timeout

$ node timeout_vs_immediate.js
immediate
timeout

Use setImmediate( )main advantage is that the call to the I / O callbacks when, no matter how many timers in the program, it adds callback function is always performed earlier than the other timers.

proess.nextTick ()

Understanding process.nextTick ()

You may have noticed that although the same as part of asynchronous API, process.nextTick( )and not displayed in the chart above, because Technically speaking it is not part of the event cycle. nextTickQueueIn the queue will be executed immediately after the implementation of the current operation, in which stage regardless of the current event loop, mentioned here refers to the underlying operations of C / C ++ handles the transition JavaScript code to be executed (strange phrase, I do not know how to translate the original is an operation is defined as a transition from the underlying C / C ++ handler, and handling the JavaScript that needs to be executed).

Let's look at the chart above, whenever you call at some stage process.nextTick( ), all incoming callback function will first be resolved before the event loop continues to execute. This may result in very serious implications, because it allows you to obstruction by recursive calls process.nextTick( )and makes the event loop generating obstruction, it can not reach the stage poll.

Why allow this to exist?

Why this bizarre situation in Node.js to be included in it? Partly because Node.js philosophy of design decisions, held that Node.js API regardless of whether it is necessary, should be executed asynchronously, for example, the following sample code fragment:

function apiCall(arg, callback) {
   if(typeof arg !== 'string')
       return process.nextTick(callback, new TypeError('argument should be string'));
}

This example parameters were examined, if the parameter type is wrong, it will be passed to the callback function of this error. This API allows process.nextTickfor additional parameters added after the callback, and supports it as a way of bubbling of an incoming call when the callback parameter, so you do not have to be achieved through the function nested.

Here we do is allow the remaining code execution transfer is completed before an error to the user. By using process.nextTick( )it to ensure that apiCall( )the method always remaining code execution and complete event loop continues to execute the callback function between these two time points. For this purpose, JS mobilize stack will allow the immediate implementation of some of the callback function and allows users to trigger in which the recursive call process.nextTick( ), but will not cause the explosion stack (call stack JavaScript engine than the set maximum capacity).

This design philosophy may cause some potential situations. For example the following example:

let bar;

// this has an asynchronous signature, but calls callback synchronously
function someAsyncApiCall(callback){callback();}

// the callback is called before `someAsyncApiCall` completes
someAsyncApiCall(()=>{
   console.log('bar',bar);
});

bar = 1;

User-defined someAsyncApiCall( )though asynchronously from the comments point of view, but in fact is a function of a synchronous execution. When it is called, callback functions and someAsyncApiCall( )the same stage is actually in the event loop, there is not any substantial asynchronous behavior, the result is, the callback function attempts to acquire barthe identifier of the scope and value although not for this variable assignment, because the remaining part of the script is not finished.

If the callback function will be replaced with process.nextTick( )the form, the rest of the script code can be executed, which makes the initialization of variables and functions statement may take precedence over incoming callback function is executed, the other advantage of this is that it does not push forward the event loop. This allows the user to some possible warning or error event is processed before the cycle continues. Such as the following examples:

let bar;

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

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

bar = 1;

Real-world scenario you will see like the following ways:

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

server.on('listening',()=>{});

When the incoming port number, it will immediately be binding. So listeningthe callback will be executed immediately, the problem is .on('listening')the callback settings did not seem to execute.

Here actually listeningsent the event is to be nextTick( )added to the queue, so the following synchronization code can be finished to be performed, such a mechanism may be provided so that the user event listener more later.

process.nextTick () Comparative setImmediate ()

These two methods of naming so many developers confused.

  • process.nextTick( )Will immediately trigger event at the same stage of the cycle

  • setImmediate( )It will trigger cycle or the next round of the event loop event tickis triggered

In fact they actually do, and their names should exchange it. process.nextTick( )Than setTimeout( )to add a callback to trigger earlier, but this is very difficult to correct historical issues, it will lead to a large number of npm package will not work correctly. There are a lot of new module released every day, which means that every passing day are likely to cause more damage, although they can cause confusion, but only a wrong wrong.

We recommend developers to stick with in the development setImmediate( ), execution timing because it is relatively easier to speculate (In addition, it also makes the code more compatible with the environment, such as browser JS).

Why process.nextTick ()

The two most important reasons are:

  1. It allows users to prioritize error, any subsequent stage of cleaning up resources no longer in use, or attempt to re-send the request before the event cycle continues.

  2. Sometimes you need to call stack is not empty to perform some of the callback function.

Such as the following example:

const server = net.createServer();
server.on('connection',conn=>{});

server.listen(8000);
server.on('listening',()=>{});

Envisaged listen()to perform at the event loop start, but listeninglistening function event by setImmediate()adding to. Unless pass hostname, otherwise the port will not be binding. For the event loop, it will arrive poll stage, if there is connection at this time already connected, then the connection will be issued in the event poll stage, but listeningevents have to wait until phase check can be issued.

Another example is the implementation of a constructor, it inherited EventEmitterand want to trigger an event in the constructor:

const EventEmitter = require('events');
const util = require('util');

function MyEmitter() {
 EventEmitter.call(this);
 this.emit('event');
}
util.inherits(MyEmitter, EventEmitter);

const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
 console.log('an event occurred!');
});

You can not immediately trigger the event in the constructor, because the script has not been executed in place of this event to add that a listener function. Therefore, the internal structure of the function you need process.nextTick( )to set the event to send statements, we can ensure that when an event is triggered, the listener has been registered, for example:

const EventEmitter = require('events');
const util = require('util');

function MyEmitter() {
 EventEmitter.call(this);

 // use nextTick to emit the event once a handler is assigned
 process.nextTick(() => {
   this.emit('event');
 });
}
util.inherits(MyEmitter, EventEmitter);

const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
 console.log('an event occurred!');
});

md original .rar

Source: Huawei cloud community original  author: do not speak great history

Guess you like

Origin blog.csdn.net/devcloud/article/details/92564378