preface

If you’ve ever gotten a Node asynchronous programming question wrong in a job interview, this article is for you. πŸ₯• πŸ… 🍚

This article is mainly a compilation of previous study notes. The purpose of writing this article is on the one hand to get familiar with the previous knowledge, on the other hand, I hope it can be helpful to students who do not know Node asynchronous programming.

This article mainly includes Node asynchronous API(non-I /O), event loop, asynchronous programming advantages and difficulties related knowledge sorted out. In the end, it sorted out how Promise and async/await solve the problems caused by asynchronous programming.

Single thread

Node maintains the single-threaded nature of JavaScript in the browser. In Node, JavaScript cannot share any state with other threads. The biggest benefit of single-threading is that it doesn’t have to worry about state synchronization as much as multithreading does, there are no deadlocks, and there are no performance costs associated with thread context exchange.

Single threading also has its own weaknesses, which you have to deal with while learning Node. By proactively confronting these weaknesses, You can take advantage of Node’s benefits and avoid potential problems so that it can be used efficiently. The weaknesses of single threading include the following three aspects.

  • Unable to utilize multi-core CPUS.
  • Errors can cause an entire application to exit, and the robustness of the application is worth testing.
  • A large number of calculations occupy the CPU, causing asynchronous I/O calls to fail.

Synchronous vs Asynchronous

In this chapter I use a chestnut from life to demonstrate synchronization and asynchrony. The wife is going to show off at noon today. We eat at 12:30 and the menu is as follows:

  • Sweet and sour fish
  • Fried green vegetables
  • cabbage

synchronous

asynchronous

conclusion

In the synchronous example, my wife was waiting for me to buy sugar, then started cooking other dishes after making sweet and sour fish. I was supposed to be ready for dinner at 12:30, and finally I had dinner at 13:00. In the asynchronous example, instead of waiting for me to buy sugar, my wife chose to make other dishes first. After I bought the sugar, I started to cook the sweet and sour fish. Finally, we had dinner at 12:30 on time.

Node Asynchronous API(non-I /O)

The timer

SetTimeout () and setInterval() in Node are the same apis as in the browser, and are used for single and multiple timed tasks, respectively. The timer created by calling setTimeout() or setInterval() is inserted into a red-black tree inside the timer observer. Each time the Tick is executed, the timer object is iterated out of the red-black tree to check if the timer time is exceeded. If so, an event is generated and its callback function is executed immediately.

The problem with timers is that they are not precise (within tolerable limits). Here’s an example:

Try it several times, and you’ll notice that the output is different each time.

Timer behavior:

process.nextTick()

Before you know about process.nexttick (), setTimeout() is used to achieve the desired effect in order to execute a task immediately, like this:

 setTimeout(()=>{
    //TODO
 },0);
Copy the code

However, due to the characteristics of the event cycle itself, the accuracy of timer is not enough. In fact, using timers requires the use of red-black trees, creation of timer objects, and iteration, so setTimeout(fn,0) is a waste of performance, while process.nexttick () is relatively light. Each time process.nexttick () is called, only the callback function is queued up for execution on the nextTick. The operation time complexity of red-black tree in timer is 0(LG (n)), and that of nextTick() is 0(1).

Try changing the cooking example with process.nexttick (FN).

setImmediate()

SetImmediate () is very similar to the process.nexttick () method in that it delays the callback. But there is a subtle difference. Look at this code:

As you can see from the results of the run, the callback function in process.nexttick () executes with higher priority than setImmediate(). Process.nexttick () is the idle observer, and setImmediate() is the check mediate. In each cycle check, idle observers take precedence over I/O observers, and I/O observers take precedence over Check observers.

Implementatively, the callbacks of process.Nexttick () are stored in an array, and the result of setImmediate() is stored in a linked list. Behaviourally, process.nexttick () executes all of the callbacks in the array in each loop, while setImmediate() executes one of the callbacks in the list in each loop. Look at this code:

process.nextTick()
setImmediate()
setImmediate()
process.nextTick()
setImmediate()
I/O

Event Loop

One soul

Look down with your answers

concept

Although JavaScript is single-threaded, event loops enable Node.js to perform non-blocking I/O operations by moving operations to the system kernel. We can understand this from the Node.js system architecture diagram.

Node.js
libuv
Node.js

When the process starts, Node creates a loop similar to while(True), which polls the task from the event queue and assigns it to a specified thread. Once a thread completes its task, it immediately returns the execution result. Each loop is called the Tick. The process of each Tick is to check whether there is an event to be processed, and its flow chart is as follows:

Phase,

  1. Timers phase, which executes the setTimeout() or setInterval() callback as early as possible when the threshold has been reached. But the running of an operating system schedule or other callbacks can delay them.

  2. Pending Callbacks phase, which executes I/O callbacks deferred until the next iteration of the loop.

  3. Idle and prepare are used only for internal use.

  4. Poll (polling) phase, retrieving new I/O events; Perform I/ O-related callbacks. This phase consists of two functions: calculating the events that should block and poll for I/O and processing the events in the polling queue. The rules are as follows:

    • If there are any that are due at this stageA timer, the timer callback is executed immediately.
    • If there is no dueA timerandPolling the queueIf it is not empty, theFIFOExecute events in sequence. ifPolling the queueIs empty, and the script is created bysetImmediate()If the alarm is scheduled, polling endspoolPhase intocheckPhase. ifPolling the queueIs empty and does not containsetImmediate(), the event loop will wait for new events to be added to the queue and then execute them immediately.
  5. The Check phase, which allows the callback to be performed immediately after the Pool phase is complete. The setImmediate() callback is executed here.

  6. Close phase, some close callbacks. If a close event is generated, the event is added to the specified queue. When the close phase is complete, this cycle ends and the next cycle begins.

SetImmediate () and setTimeout ()

SetImmediate () is actually a special timer similar to setTimeout(), but the behavior depends on the call time.

  • setImmediate()In the currentpollingExecute the script when the phase is complete.
  • setTimeout()Plan to execute the script after the minimum threshold is reached.

The order in which timers are executed varies from context to context, as shown in the following two pieces of code.

  1. Run in the main module, output order is limited by process performance:

Run it a few more times, and you’ll notice that the sequential output order changes.

  1. In an I/O cycle, the command is always executed firstsetImmediate()

Node asynchronous programming advantages and difficulties

advantage

The biggest feature of Node is the event-driven I/O model. Non-blocking I/O enables CPU and I/O to be better utilized without relying on each other.

The difficulties in

Exception handling

A try/catch cannot catch an exception thrown in a callback, for example:

Node

  async ((err,result) => {
     if (err){}
  });
Copy the code

Callback pyramid

You need to read the contents of the file with the suffix readme. TXT in the specified directory, modify the contents, and verify whether the modification is successful. The code:

The multiple layers of callbacks not only make the code difficult to read, but also difficult to maintain once requirements change.

Asynchronous programming solutions

Promise

concept

Promise is a solution to asynchronous programming that is more reasonable and powerful than the traditional solution of events and callback functions.

A Promise object is a proxy object (proxy for a value), and the proxied value may not be known at the time the Promise object is created. It allows you to bind handlers for success and failure of asynchronous operations. This allows asynchronous methods to return values as synchronous methods do, but instead of immediately returning final execution results, a Promise object that represents future results.

Promises have the following states:

  • Pending: The initial state, which is neither successful nor failed.
  • Depressing: Represents the successful completion of the operation.
  • Rejected: indicates that the operation fails.

The characteristics of

Promise objects have the following two characteristics:

  • The state of the object is not affected by the outside world. Only the result of the asynchronous operation can determine the current state, and no other operation can change the state.
  • Once the state changes, it’s never going to change, and you can get this at any time.

Chain calls

Since both the promise.prototype. then and promise.prototype. catch methods return a Promise object, they can be called chained.

In actual combat

Encapsulate asynchronous callbacks

If the file path exists, the file contents are printed. Otherwise, the printing is abnormal.

This action is performed regardless of the final state of the Promise object.

3. Promise will be executed immediately after it is created

4, Promise. All ()

Wrap multiple Promise instances into a new Promise instance. The new Promise state will become a big pity only when all the Promise instance states become fulfilled. Once there is a Promise state (Rejected), the new Promise state changes to Rejected.

The promise.race () method again wraps multiple Promise instances into a new Promise instance. If the state of one of the multiple Promise instances changes first, the state of the new Promise instance will change.

disadvantages

  • PromiseOnce created, it is executed immediately and cannot be cancelled.
  • If you do not set the callback function,PromiseErrors thrown from the inside are not reflected externally.
  • When inpendingPhase, there is no way to know the current stage of progress.

async/await

concept

An asynchronous function can contain an await instruction, which suspends the execution of the asynchronous function, waits for a Promise to execute, and then continues executing the asynchronous function and returns the result.

The await keyword is valid only within asynchronous functions. If you use it outside of an asynchronous function, it throws a syntax error.

In actual combat

1, replace promise.then ()

2. Catch exceptions

reference

MDN Promise MDN Async ECMAScript 6 Getting started Event Loop