This article was inspired by Chapter 3 of Dive into Node.js

Look at the microtasks

Here’s a common boring interview question:

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

new Promise((resolve, reject) => {
  console.log(2);
  for (let i = 0; i < 10000; i++) {
    i === 9999 && resolve();
  }
  console.log(4);
}).then(() => {
  console.log(5);
});

console.log(6);Copy the code

A close look at tasks and microTasks in JavaScript will give you a quick answer. In Chrome 60.0, the output sequence is 2, 4, 6, 5, 1.

There are two main points to this question:

  • PromiseBelong tomicrotaskAnd thesetTimeoutThe callback istaskforJavaScriptIn terms of engines,event loopThe priorities are different, soPromiseWill resolve beforesetTimeoutThe callback output of
  • PromiseThe constructor arguments are called synchronously, so 2 and 4 are printed before 6

Is Node single threaded

It is commonly understood that JavaScript engines are single-threaded, so the question is, how do you handle asynchronous I/O, network request logic without multithreading?

In fact, we know that in Node.js, we use asynchronous I/O, by letting some threads do blocking I/O or non-blocking I/O plus polling technology to complete the data acquisition, let a thread do calculation, through the communication between threads I/O data transfer, this asynchronous I/O way is thread pool.

So, for Node.js, all of this asynchronous logic is implemented by thread pools, so it’s not accurate to say that Node is single-threaded, just for the JavaScript code you’re writing.

Event loop

When the Node.js process starts, Node.js creates a loop similar to while(true), and executes the body of the loop each time we call it a Tick. Each Tick checks to see if there are any events to handle, and if so, pulls out the event-related callback function. If there are associated callback functions, they are executed. It then enters the next loop and exits the process if there is no more event handling.

During each Tick process, how do you determine that an event needs to be handled? The concept introduced here is the observer. Each event loop has one or more observers, and the process of determining if there are events to be processed is to ask those observers if there are events to be processed.

In Node, events are mainly generated from network requests and file I/O. The observers of these events include file I/O observers and network I/O observers. The observers classify the events.

The event loop is a typical producer/consumer model. Asynchronous I/O, network requests, and so on are the producers of events, providing Node with a continuous stream of different types of events, which are passed to the corresponding observer, and the event loop takes the event from the observer and processes it.

At this point, the first phase of the asynchronous invocation at the JavaScript thread level is over, and the JavaScript thread can continue to perform subsequent operations on the current task. The current I/O operation is waiting to execute in the thread pool, and whether or not it blocks I/O does not affect subsequent execution of the JavaScript thread. Once the task in the thread pool completes, the JavaScript thread is notified to execute the relevant callback.

The process.nexttick () method and the setImmediate() method

In addition to the setTimeout and setInterval non-I /O asynchronous apis of JavaScript, Node.js also has the Process. nextTick and setImmediate methods.

Process. nextTick is not an alias for setTimeout(fn, 0). It’s more efficient. Event polling is followed by Tick calls that run before any I/O events (including timers).

SetImmediate also delays a callback, and callback functions are executed in the order in which they were created. Each iteration of the event loop processes the entire callback queue. If an immediate timer is queued by an executing callback, the timer is not fired until the next iteration of the event loop.

If callback is not a function, TypeError is raised.

NextTick is the idle observer, and setImmediate is the Check observer. In each event cycle, the IDLE observer precedes the I/O observer, and the I/O observer precedes the Check observer.

Take a look at this example:

Process.nexttick (() => {console.log('nextTick delay 1'); }); Process.nexttick (() => {console.log('nextTick delay 2'); }); SetImmediate (() => {console.log('setImmediate '); Process.nexttick (() => {console.log(' strong insert '); }); }); SetImmediate (() => {console.log('setImmediate '); }); Console. log(' normal execution ');Copy the code

This code is excerpted from Node.js by Ling Pu, but when I do it, the result is not the same as the original because it is now Node 8.4.0

The reader can run it down to get a sense of the priorities of several observers.

High-performance Node Server

Generally speaking, the server model is divided into the following types:

  • Synchronous services can process only one request at a time while all other requests are in a waiting state.
  • Multithreaded services such asApacheHowever, the creation, destruction and switching of threads consumes CPU resources and is not as efficient as single-threaded services.
  • Event-based services such asNode.js,Nginx, there is no need for thread context switch, consuming less CPU resources.

However, compared to Nginx, despite its own reverse proxy and load balancing mechanism, its performance is still inferior to that of Node.js servers.