Browser event ring and Node event ring have to tell the story!

Before entering text, first of all to thank you for your bosses to my first article on the nuggets’ ES6 version of the Promise, give you a different experience, “the affirmation and correct, may write unsatisfactory, but your thumb up is one of my continue to share power, as long as you work hard, don’t too care about the results, because the result of the effort will make you satisfied! Share with you!

Well, without further ado, let’s move on to today’s topic. Today we’re going to talk about what is an event ring? What’s the difference between a javaScript event loop and a Node event loop? There is a feeling of don’t know how to start, don’t worry, as long as you carefully read this article, I believe can solve the doubts in the heart.

First, learn some common concepts

As the saying goes, to do a good job, you must first sharpen your tools. Before getting into the browser event loop and Node event loop scenarios, it’s important to understand a few common concepts.

There is a heap and a stack.

Stack is a concept that cannot be ignored in the field of computer. If you want to know more about it, please go to stack _ Baidu Encyclopedia. In javaScript, basic data types are stored in the stack, and memory space is automatically allocated and freed; The heap stores reference data types and is dynamically allocated memory, which varies in size and is not automatically freed.

  • heapHeap: also called heap memory; Is a queue first, first in, first out data structure;
  • stackStack: Aka ‘stack ‘, is also a data structure, but it stores data on a fifo basis.

Hee hee 😝, I spent a long time (exaggerated) painting, feel good.

Now that we have a general understanding of the meaning of heap and stack, let’s look at an interview question, how to use JS code to implement the function of queue and stack? In fact, very simple, is the array of the most basic common add and delete method.

  • Methods for implementing queues (fifO)
let arr = new Array();
arr.push(1);
arr.push(2);
arr.shift();
Copy the code
  • How to implement the stack (first in last out)
let arr = new Array();
arr.push(1);
arr.push(2);
arr.pop();
Copy the code

2. Threads and processes

First, we should know that processes are larger than threads. A program must have at least one process, and a process must have at least one thread. For example, let’s take a look at this image:

  • Rendering engine: The rendering engine is multi-threaded internally, containing the two most important threads, THE UI thread and the JS thread. It is important to note that the UI thread and the JS thread are mutually exclusive, because the results of the JS run affect the results of the UI thread. UI updates are stored in the queue and executed as soon as the JS thread is idle.
  • Js single thread:JavaScriptThe biggest feature is single threaded, in fact the main thread is single threaded. Why do you say that? If you think about it, if JS is multi-threaded, we have a page where one thread has to delete that element, and another thread has to keep that element. That’s whyJavaScriptExecuting synchronous code does not block the execution of asynchronous code.
  • Other threads:
    • Browser event trigger thread (used to control the event loop, storesetTimeout, browser events,ajaxThe callback function of
    • Timed trigger thread (setTimeoutTimer thread)
    • Asynchronous HTTP request threads (ajaxRequest thread)

Macrotasks and microtasks

Both macro and micro tasks are asynchronous tasks. If you know the vUE source code, you should know the concepts of macroTask and microtask, their execution timing is different. Vue’s $nextTick source code is implemented through macro tasks and micro tasks. (Check out Vue’s Github to see how this works.)

  • Common macro tasksmacrotaskThere are:setTimeout,setInterval,setImmediate(Ie only supports it,node implements it itself),MessageChannel
  • Common microtasksmicrotaskThere are:promise.then(),process.nextTick(node)

2. JavaScript event loops

Browser, event loop running mechanism is that the contents of the stack to be executed, the contents of the stack to perform after executing the task, the task to empty again after macro mission, first take a macro task, executing the task, and then take the macro task clear micro tasks this constant cycle, we can see the image below to understand:

As you can see from the figure, synchronous tasks enter the execution stack and asynchronous tasks enter the callback queue for execution. Once the contents in the execution stack are finished, the waiting tasks in the task queue are read and put into the execution stack to start execution. (Missing microtasks)

So, to test the interview question, when we run the following code in a browser, what is the output?

setTimeout(() => {
    console.log('setTimeout1');
    Promise.resolve().then(data => {
        console.log('then3'); }); }, 1000); Promise.resolve().then(data => { console.log('then1');
});
Promise.resolve().then(data => {
    console.log('then2');
    setTimeout(() => {
        console.log('setTimeout2'); }, 1000); }); console.log(2); // Output: 2then1 then2 setTimeout1  then3  setTimeout2
Copy the code
  1. The stack is executed first, that is, the sync code, so 2 is printed;
  2. And then you clear the microtasks, so the output in turn isthen1 then2;
  3. Since the code is executed from top to bottom, so after 1ssetTimeout1Executed output;
  4. And then clearing the microtask again,then3Be output;
  5. Finally execute outputsetTimeout2

Node’s event loop

Node is a V8 engine based JavaScript operating environment, which has obvious advantages in dealing with high concurrency and I/O intensive (file operations, network operations, database operations, etc.) scenarios. Node’s event loop mechanism is not quite the same as the browser’s. In the Node runtime environment:

  1. The JS code we write is handed over to the V8 engine for processing
  2. NodeApi may be called in your code, and Node will be calledlibuvTo deal with
  3. libuvAsynchronous I/O through blocking I/O and multithreading
  4. Then, in an event-driven way, we put the results into an event queue, and finally deliver them to our application.

The essence is that there is an event loop mechanism inside Libuv, a high-performance, event-driven I/O library. The event loop is initialized when Node is started.

event loop

  • timersTimer: ExecutesetTimeout,setIntervalThe callback function of;
  • I/O callbacks: performI/O callbackDeferred to the next stage of execution;
  • idle, prepare: Queue movement, internal use only
  • pollPolling: Retrieves new I/O events; Perform I/ O-related callbacks
  • check: performsetImmediateThe callback
  • close callbacks: performcloseThe eventcallback, e.g.socket.on("close",func)

Ok, let’s start with a simple quiz question:

setTimeout(function () {
    console.log('setTimeout');
});
setImmediate(function () {
    console.log('setImmediate');
});
Copy the code

If you run this problem a few times in the Node environment, you’ll notice that the output order is not fixed. The timers queue precedes the check queue, but setTimeout and setImmediate mediate do not have a clear sequence, resulting from node’s setup time.

Corresponding to the above exercise, let’s look at the following problem:

let fs = require('fs');
fs.readFile('./1.txt'.function () {
    setTimeout(() => {
        console.log('setTimeout');
    }, 0);
    setImmediate(() => {
        console.log('setImmediate');
    });
});
Copy the code

So the output sequence is setImmediate and then setTimeout, no matter how many times you run it, the output sequence doesn’t change. This is because fs file operations (I/O operations) belong to the poll phase, and the next phase of the poll phase is the check phase, so the output order is not in doubt.

Finally, let’s look at another interview question to deepen our understanding of Node event rings:

setImmediate(() => {
    console.log('setImmediate1');
    setTimeout(() => {
        console.log('setTimeout1')}, 0); }); Promise.resolve().then(res=>{ console.log('then');
})
setTimeout(() => {
    process.nextTick(() => {
        console.log('nextTick');
    });
    console.log('setTimeout2');
    setImmediate(() => {
        console.log('setImmediate2');
    });
}, 0);
Copy the code

SetTimeout2 nextTick setImmediate1 setImmediate2 setTimeout1 The output of the nextTick microtask is because the timers queue switches to the check queue. The output of setImmediate1 and setImmediate2 is because the next pair of columns can be entered only after the current queue is completed.

Conclusion: That’s all for today’s topic. There may be something unclear or wrong in this article, please correct it. I will continue my efforts to share it every week. Thank you very much! If you feel that read this article has gained, please do not forget to move a small ❤️ oh! Let us play double OARS together, progress a little bit every day, in the technology of the road farther and farther!

Original is not easy, reprint please indicate the source! Thank you very much!