Preface: Hello everyone, my name is Shao Weiru, everybody likes call me little shao, study financial professional with interests into the pit of the program apes, buy the first book of vb and from university self-study vb, I has been wedded with programming, easy language to write the game then self-study auxiliary, trading software, has entered the front field, see a lot of friends to write articles to share, oneself also get a play, The following article is purely personal understanding, easy to record learning, there must be misunderstanding or understanding is not in place, intended to stand on the shoulders of predecessors, share personal popular understanding of technology, grow together!

In the future, I will gradually update javascript. I will try my best to write all the learning path system of javascript, including es6, Angular, React, Vue, NodeJS, KOA, Express, and public accounts, which are commonly used in the front end, from shallow to deep. Hope that we can harvest, also hope that you pay attention to me ~

List of articles: juejin.cn/user/372040…

Author: Weiru Shao Email: [email protected] Wechat: 166661688 Github: github.com/iamswr/


I’m going to write notes for nodeJS serialization, This article focuses on the problems nodeJS solves and the advantages, the concept of process and thread, the concept of synchronous and asynchronous, the concept of blocking and non-blocking, the concept of queue and stack, macro and micro tasks, and the very important browser event loop and NodeJS event loop.


What problems does Node solve and what are its advantages?

We front-end and back-end interaction, mainly request an interface or let the back-end to return a page, frequent IO operations, the biggest bottleneck of Web services is to deal with high concurrency (the number of concurrent access to the server at the same time), while Node is in high concurrency, IO intensive scene, has obvious advantages.

I/O intensive operations refer to file operations, network operations, and read operations. CPU intensive refers to the need for a large number of logical processing operations, encryption and decryption, compression and decompression operations;

What is node?

Node.js is a JavaScript runtime based on Chrome V8 engine.

Although Node uses javascript syntax, it is not completely javascript. We know that javascript contains ECMAScript, DOM, and BOM. Node does not contain DOM and BOM, but it also provides a series of modules for us to use. Such as HTTP, FS module.

Node uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. We’ll be exposed to a lot of third-party module packages during development, and Node has the largest open source library ecosystem in the world.

Event-driven: After an event is sent, it is notified by the callback notification mechanism. Non-blocking I/O: for example, a file is read asynchronously in a non-blocking manner.


Processes and threads

Suppose we use Java, PHP, etc

Generally, the server initiator uses Tomcat (Apache) and IIS, which belongs to multi-threaded synchronization blocking, and then the number of threads will be configured when starting the service.

Mysql, Mongo, Redis and so on are databases.

Typically, the client sends a request to the server, the server operates on the database, and the database sends the data back to the server, which then returns it to the client.

Initiated when a client requests to the server, the server will have a thread to handle the request, if be tomcat iis belongs to multithreaded synchronization block, such as when the service will configure the number of threads, and then through the server sends a request to the database data, as the thread will always wait for database data back, When the database returns data to the server, the server returns data to the client.

When the number of concurrent requests exceeds the number of threads, the following requests will wait for the completion of the previous request to be executed. After the completion of the thread, it is not immediately destroyed and then created, but after the completion of the last request, it will be reused to the next request.

The outer square shown in the figure is the process, and the inner square is the thread. A process can allocate multiple threads. In our actual development, a project is generally multi-process.

So what is a process? Process is the basic unit of the operating system to allocate resources and schedule tasks. Thread is a program running unit built on the process. There can be multiple threads on a process.

Suppose we use Node

So nodeJS, we say single-threaded. We don’t mean that only one thread can run in a process, but the main thread is single-threaded. Node looks like this.

When the client sends a request at the same time, the first after the request is sent to the server will have a thread processing, the server will request database, thread doesn’t like it that way at this time, waiting for the return of data, the thread but to deal with the second request, returned to the first data when the database, the thread through mechanisms such as the callback, the event loop.

Although Node is single-threaded, you can use setTimeout to start multiple threads, which can be played with when concurrency is high.

But the node is not a scene can be used, for cpu-intensive scenario, but not very practical, cpu-intensive “refers to a large number of logic, calculation, such as a large number of calculations, such as compression, encryption, decryption (this part of the c + +), the node is suitable for the IO operations (IO) dense, why do you say the node for the front? Because the front end is basically asking for an interface, or the server rendering a page back, Node is ideally suited for this scenario.

We often use Node as an intermediate layer, where the client accesses Node and Node accesses the server, such as the Java layer, where Java returns data to Node and Node returns data to the client.

Common Java is synchronous multithreading, node is asynchronous single thread, if Java concurrency is 10 million, then Node concurrency can reach more than 3 times.

So let’s take a look at the browser processes and threads that often work with the front end

  • User Interface (User Interface process) : address bar, label, forward and backward, etc.
  • The main process of the Browser: communicates instructions between the user interface and the rendering engine;
  • Data Persistence: Store cookies, sessionStorage, loaclStorage, indexedDB, etc.
  • Rendering engine: The Rendering engine is multi-threaded internally, including Networking (Ajax requests), JavaScript Interpreter (JS threads), and UI Backend (UI threads).

In the rendering engine, it is important to note that there are two very important threads inside it, namely the JS thread and the UI thread. The JS thread and the UI thread are mutually exclusive and share the same thread.

So why are JS threads and UI threads mutually exclusive? Why single thread? We can imagine that when we operate a DOM node through JS, if it is executed at the same time, there will be a difference between speed and slowness, which will appear very chaotic. Then imagine that, if it is multi-threaded, multiple threads operate the same DOM node, is it also very chaotic? So JS is designed to be single threaded.

In Java, if multiple threads access the same resource, a lock will be added to the resource. This is similar to when class is over, multiple students go to the bathroom, and there is only one bathroom. The first one locks the door, and the others have to wait in line.

Js single thread means that the main thread of JS is single thread.

There are other threads in the browser
  • The browser event triggers the thread
  • Timer trigger thread
  • Asynchronous HTTP requests trigger threads

Asynchronous and synchronous, blocking and non-blocking

It is mainly divided into the following categories

  • A synchronized block
  • Asynchronous blocking
  • Synchronous nonblocking
  • Asynchronous nonblocking

Assume that the caller is Xiao Ming and the called is Xiao Hong figure 1: Like little red xiaoming, xiao Ming and decided to call the little red, I love the little red answer the phone, if the little red hang your telephone there, xiao Ming has two states, one is a block, block, a kind of dispute blocking is little red dry phone call at the same time, xiao Ming is still in waiting, called block, non-blocking is little red dry phone at the same time, xiao Ming can do other things, is called a non-blocking, After xiao Hong answered the phone, she said, “I’ll think about it and give you a reply. If I didn’t hang up the phone at this time, it would be synchronous. If I hang up the phone and tell Xiao Ming after a while, it would be asynchronous.

Figure 2: Said after the little red answer the phone, think about it, I’ll tell you the result, then hang your phone, this belongs to the asynchronous, then xiao Ming if also devotes herself to wait for the phone to reply (i.e., 2.1), so called blocking, if xiao Ming at this time is not waiting for the answer, but a call to another sister (i.e., 2.2), so called nonblocking, The combination is asynchronous blocking or asynchronous non-blocking.

Figure 3: When Xiao Hong answers the phone, she says, “Think about it, I’ll tell you the result later.” Then she doesn’t hang up and keeps talking, which belongs to synchronization. However, Xiao Ming secretly calls another girl to confess her love, which belongs to non-blocking.


Queue and stack

  • Characteristics of queues: Queues are characterized by first in, first out, such as groups, which are added later.

  • The characteristics of the stack is advanced after out

First we put 1, 2, 3 on the stack, and then when we take it out, we take it out by 3, 2, 1

function a() {
  function b() {
    function c() {} c()} b()} a() {} c()} b()} a(); B then loses its execution stack // which is the execution context, so in the execution stack, it is first and last out.Copy the code

Macro task, micro task (both asynchronous operation, temporarily using browser event loop mechanism)

We all know async, but within async, there are two main categories: macro tasks and micro tasks. In the browser event loop, micro tasks are performed before macro tasks are executed.

Common macro tasks:

  • setTimeout
  • SetImmediate (Only supported by IE)
  • setInterval
  • messageChannel

Common microtasks:

  • Promise.then()
  • mutationObserver
When we use VUE, we have a nextTick method, which means to insert a method into the next queue. Can we see how it is implemented in the source code

Source: github.com/vuejs/vue/b…

if (typeof setImmediate ! = ='undefined' && isNative(setImmediate)) {
  macroTimerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else if(typeof MessageChannel ! = ='undefined' && (
  isNative(MessageChannel) ||
  // PhantomJS
  MessageChannel.toString() === '[object MessageChannelConstructor]'
)) {
  const channel = new MessageChannel()
  const port = channel.port2
  channel.port1.onmessage = flushCallbacks
  macroTimerFunc = () => {
    port.postMessage(1)
  }
} else {
  /* istanbul ignore next */
  macroTimerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}
Copy the code

In this code, you can see that Vue’s nextTick handles the macro task first by determining whether setImmediate exists, then by determining whether MessageChannel exists, and then by demoting to setTimeout if not.

if(typeof Promise ! = ='undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  microTimerFunc = () => {
    p.then(flushCallbacks)
    if (isIOS) setTimeout(noop)
  }
} else {
  // fallback to macro
  microTimerFunc = macroTimerFunc
}
Copy the code

In this code, you can see that Vue’s nextTick handles microtasks first by determining whether there is a Promise, and then demoting it to a macro task if there is no Promise.

setImmediate

Let’s take a look at how this code performs (note that setImmediate requires Internet Explorer to open)

<! DOCTYPE html> <html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <script>
    setImmediate(function(){
      console.log('I am setImmediate')
    },0)

    Promise.resolve().then(function(){
      console.log('I am a Promise')
    })
    console.log('I'm sync code')
  </script>
</body>
</html>
Copy the code

Print ‘I am sync code’ -> ‘I am Promise’ ->’ I am setImmediate’ to see that the sequence is sync code, then the microtask code, then the macro task code.

MessageChannel

The following code prints “I’m synchronous code” -> “Hello SWR” in turn, since MessageChannel is also a macro task.

<! DOCTYPE html> <html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <script>
    let messageChannel = new MessageChannel()
    let port1 = messageChannel.port1
    let port2 = messageChannel.port2
    port1.postMessage('hello swr')
    port2.onmessage = function (data) {
      console.log(data.data)
    }
    console.log('I'm sync code')
  </script>
</body>
</html>
Copy the code

MutationObserver

MutationObserve is used to monitor DOM node updates. For example, if you have a requirement to perform some behavior after DOM insertion is complete, you can do this.

First, it prints “I’m synchronous code”, then executes two for loops to insert the DOM node, and finally, when the DOM node is updated, it prints “Insert completed 100”.

<! DOCTYPE html> <html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <div id='div'>
    
  </div>
  <script>
    let observe = new MutationObserver(function(){// The number of p is printed in this step to verify that the DOM node is inserted before performing this step. console.log('Insert done',document.querySelectorAll('p').length)
    })
    observe.observe(div,{childList:true})
    console.log('I'm sync code')
    for(leti = 0 ; i < 50; i++){ div.appendChild(document.createElement('p'))}for(leti = 0 ; i < 50; i++){ div.appendChild(document.createElement('p'))
    }
  </script>
</body>
</html>
Copy the code

How are macro and micro tasks performed

Let’s start with a piece of code

setTimeout(() => {
  console.log('I am setTimeout1')
  Promise.resolve().then(()=>{
    console.log('I am Promise1')})}, 0); Promise.resolve().then(()=>{ console.log('I am Promise2')
  setTimeout(() => {
    console.log('I am setTimeout2')}, 0); Promise.resolve().then(()=>{ console.log('I am Promise3')})})Copy the code

SetTimeout is a macro task and promise.resolve ().then() is a microtask.

Another concept is need special attention, and it is also a node, js, in browser event loop mechanism, after the execution stack synchronization code to empty, system will go to read the task queue, which will read first task, the micro task after emptying, again read macro task in turn, pay special attention to here, is not a one-time finish all macro tasks, Instead, it is like a queue, which first takes a macro task to execute, then checks to see if there is a microtask, and if so, executes the microtask, and then reads another macro task to execute, repeating the loop.

It should be noted that in nodeJS event loop mechanism, microtasks are executed first. However, when a microtask is executed and a macro task is executed, even if there are new microtasks in the macro task execution process, the microtasks are not executed first, but are executed in the macro task queue.

First, the code executes from top to bottom, when it hits setTimeout1, it’s thrown into the macro task queue, and then it executes down to the macro task queue when it hits Promise2, then when the stack is finished, it executes Promise2 first, prints “I’m Promise2”, and when it’s executed, It’s got setTimeout2 in it, it’s gonna put setTimeout2 in the macro task queue, it’s gonna run down, it’s gonna put Promise3, it’s gonna put Promise3 in the microtask, it’s gonna execute, it’s gonna print “I’m Promise3”, and then the microtask queue is done, The macro task reads setTimeout1, prints “I am setTimeout1”, continues to execute, and finds a “Promise1” in the macro task, executes “Promise1”, and prints “I am Promise1”. When the microtask queue is empty again, go to the macro task queue and fetch setTimeout2 and execute, printing “I am setTimeout2”.

Print ‘I am Promise2′ ->’ I am Promise3′ -> ‘I am setTimeout1′ ->’ I am setTimeout2′


The Event Loop of the browser Event Loop

In javascript, there are two parts: heap and stack. The general stack can also be the execution stack and execution context. When we operate in the stack, we will send some ajax requests, timers and so on. So how does this WebAPIs multithreading work on the stack?

For example, after the Ajax request is successful, the Ajax callback will be put into the callback queue. Then, after all synchronization tasks are executed in the execution stack, the system will read the events in the queue and put them into the execution stack one by one. If there are synchronization tasks in the execution stack, Then the tasks in the synchronization task will be executed and then the events in the read queue will be executed in the execution stack. This process is continuous loop.

Conclusion:

  1. All synchronization tasks are executed on the main thread, forming an execution stack.
  2. If there is an asynchronous task in the execution stack, the asynchronous task will be placed in the task queue after it has a running result.
  3. If the synchronization task in the execution stack is completed, events are read from the task queue to be executed in the execution stack.
  4. The execution stack reads events from the task queue in a continuous loop;

Let me give you some examples

// We have threesetTimeout
setTimeout(() => {
  console.log('a')}, 0);setTimeout(() => {
  console.log('b')}, 0);setTimeout(() => {
  console.log('c')}, 0); console.log('hello swr') // First the code is executed from top to bottom, encounteringset// If there are results (remember, run results!) // The synchronization code console.log('hello swr'// Print it out first'hello swr', and then the synchronization code in the stack has been executed, then go to the task queue // fetch these three in turnsetTimeout events are executed and printed out in sequence'a' 'b' 'c'// This sequence is never out of order because it follows the event loop mechanismCopy the code

So why does the synchronous code in the execution stack execute the task in the task queue after it completes? Next we can look at this example. If we write an infinite loop in synchronous code, will the events in the task queue still be executed?

setTimeout(() => {
  console.log('a')}, 0);setTimeout(() => {
  console.log('b')}, 0);setTimeout(() => {
  console.log('c')}, 0);for(;;) {} // in an infinite loop, we find that it will never print'a' 'b' 'c'// Because the synchronization code is in an infinite loop, it is always in the execution state, and the synchronization code in the execution stack is not finishedCopy the code

Nodejs Event Loop of Event Loop

Nodejs also has its own event loop, which is not the same mechanism as the browser event loop. The application code we write usually runs in V8 cause, which is not just V8 stuff, such as setTimeout and Eval. We also write code based on some Node APIS, such as Node.js Bindings, for example, node FS module, which can send some asynchronous IO operations, but node async is different from browser, it has its own LIBUV library, specially processing asynchronous IO operations. Each time we call the Node API, it will call multiple threads into LIBUV and block the call synchronously, which simulates the asynchronous mechanism. After success, the callback execution will be put into a queue. And then it goes back to our client.

Nodejs event loop, which divides each stage clearly, because nodeJS has the Libuv library, which has these aspects, each of which is a queue.

In 4, the I/O queue in the poll is constantly polled and checked to see if the timer is up, and if so, the event is switched from the queue to 1.

In 5, only setImmediate is stored. If 4 detects a check phase while polling, it moves down to the check phase.

Execution order

  • First, execute the code in the stack;
  • If there are events in the microtask, all queues in the microtask are completed.
  • Execute the queue in 1;
  • And then in turn perform a queue, it is important to note that here for the processing of micro tasks, and browser event loop mechanism is different, such as node when performing 1 in the queue, is executed in sequence, even if the way there is a new task, also won’t executing the task, but when the queue after the execution, switch to the next queue before executing the task;
  • A switch between queues performs a microtask;
  • The process is cyclical;

As you can see from this code, when executing on queue 1 in node’s event loop, even if there is a new microtask, the microtask will not be executed immediately, but will only be executed after the current queue has been emptied.

setTimeout(() => {
  console.log('setTimeout1')
  process.nextTick(()=>{
    console.log('nextTick')})}, 0);setTimeout(() => {
  console.log('setTimeout2')}, 0); // Print the output in sequence'setTimeout1' -> 'setTimeout2' -> 'nextTick'
Copy the code

Question area for friends: