Home  >  Article  >  Web Front-end  >  Similarities, differences and partial mechanisms of EventLoop between browsers and NodeJS

Similarities, differences and partial mechanisms of EventLoop between browsers and NodeJS

不言
不言Original
2018-07-07 17:23:572018browse

This article mainly introduces the similarities, differences and partial mechanisms of EventLoop between browsers and NodeJS. It has certain reference value. Now I share it with you. Friends in need can refer to it

Browser Similarities and differences with NodeJS's EventLoop, as well as some mechanisms

javascript is a single-threaded scripting language. Although it is single-threaded, it has many asynchronous APIs to help developers solve thread blocking problems. For example: onClick registered callback function, essential ajax, etc... But how does the JavaScript running environment achieve single-threading without blocking the thread all the time and waiting for various asynchronous operations to complete before continuing to execute the operation?
The answer is: event loop

1.event loop 的规范是在HTML5中规定的。
2.event loop 是 javascript 运行环境(手动加粗) 的机制。
3.浏览器实现的event loop 与 NodeJS 实现的event loop 是有异同的。
Event loop specification link defined in HTML5 https://www.w3.org/TR/html5/w...


A browser’s event loop

1. Briefly understand

event loop is the event loop, what is it? What is the structure? Teacher Ruan Yifeng's blog has a picture. Although it is very straightforward and clear, it lacks some things and cannot fully demonstrate the overall circulation mechanism of the event loop. Let’s take a look at the pictures first:
Similarities, differences and partial mechanisms of EventLoop between browsers and NodeJS

The pictures are not original by the author and come from Ruan Yifeng’s blog. Please note that they have been infringed and deleted.

We can get the information from the picture:

1. The javascript engine executes javascript in a single thread, because there is only one stack in which there are various executing and waiting processes. Executed event.
2. Some webAPIs put the callback generated during execution into a queue, that is, the "event queue".
3. In the event loop, events waiting for execution in the "event queue" are continuously pushed into the javascript execution stack.

This is the mechanism of event loop simplification. Why is it said to be simplified? Because there are many operations and rules that are not mentioned in the loop.

I won’t give chestnuts, but I will give an analogy.

Similarities, differences and partial mechanisms of EventLoop between browsers and NodeJS

Similarities, differences and partial mechanisms of EventLoop between browsers and NodeJS

Let’s talk about a common question (it’s inconvenient to edit the article, so it’s just one line. If you’re a line-changer, you’re welcome) Hit me!)

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

new Promise((res,rej)=>{ res() }).then(e=>{ console.log(2) });

They are also asynchronous APIs provided in javascript, and they are also executed directly (what developers hope, although it will cause delays due to blocking to prevent leverage), but regardless of this Whoever goes up or down the two lines of code will have the output 2 1. Because this involves the execution order and rules of macro task and micro task in the event loop.


2. Overall process
Going back to the problem that the flow chart just mentioned is not perfect enough, now let’s look at a complete and comprehensive event loop flow chart.
Similarities, differences and partial mechanisms of EventLoop between browsers and NodeJS

The picture is not original by the author, it comes from secrets of javascript ninja, please note that it will be infringed and deleted.

This is a complete flow chart of event loop. From the picture, we can see many nouns that were not mentioned just now. Let’s sort it out from beginning to end (from top to bottom):

1. Read the tasks in the Macrotask queue. There are two situations
  • The task queue is empty, and the downward execution

  • The task queue is not empty, and the first entered (Manual article bold) The task is pushed into the javascript execution stack and executed downwards

2. Read the tasks in the Microtask queue. There are two situations
  • The task queue is empty, execute downwards

  • The task queue is not empty, push the first entered task javascript execution stack, and repeat this operation again (manual article bold) until the Microtask queue is empty. To put it bluntly: Push all tasks from this task queue into the javascript execution stack in order, and execute downward

3. According to this time Cycle time consumption (manual article bold) Determine whether needs and whether can update the UI [This cycle time issue will be mentioned later]
  • No need, repeat the first step

  • If necessary, proceed downwards

4. Update UI, UI rendering, and block at the same time javascript execution. And continue to repeat step one.

The above is an entire event loop process. From the process we can see that there are two "task queues". The API for these two queues to be instantiated into javascript is

Macrotask queue --> setTimeout || setInterval || javascript代码

Microtask queue --> Promise.then()

At this point, a complete event loop process is completely finished.

3.实例解析
什么鬼?这么复杂? 弄懂?不存在的
Similarities, differences and partial mechanisms of EventLoop between browsers and NodeJS

现在回到刚才提到的 “老生常谈的问题” 从实例的角度来说明一下问题。我们假设这个 javascript 文件叫做 "main.js"
"main.js"中的代码(+ 为自定义标记)

+1 console.log(1);

+2 setTimeout(e=>{ console.log(2); },0)

+3 setTimeout(e=>{ console.log(3); },0)

+4 new Promise((resolve,reject)=>{ console.log(4); resolve();})
.then(e=>{ console.log(5); })

+5 setTimeout(e=>{ console.log(6);

  +6 new Promise((resolve,reject)=>{ console.log(7); resolve(); })
     .then(e=>{ console.log(8);})
})

那么这个执行顺序是怎样呢?从头带尾梳理一遍(词穷,全文只要是流程统一是“从头到尾梳理一遍”)

macrotask: javascript 代码,所有同步代码执行。输出:1 4。注册 +4 到 microtask。 注册+2 +3 +5 到 macrotask。
microtask: 执行 +4 输出:5
macrotask: 执行 +2。 输出 2
microtask:
macrotask: 执行 +3。 输出 3
microtask:
macrotask: 执行 +5。 输出 6 7。 注册 +6 到 microtask。
microtask: 输出 8

所以总体输出的顺序为:1 4 5 2 3 6 7 8

如果这个输出与你所想相同,那么基本就没有问题了。
那么如果不对或者有问题怎么办?
Similarities, differences and partial mechanisms of EventLoop between browsers and NodeJS

PS: 前面提到 【本次循环耗时】这个问题,这里我也不是非常清楚,望大牛指点。浏览器一般渲染页面60/S,以达到每秒60帧(60 fps),所以大概16ms一次,既然有了时间我们不经就会问?前面的任务处理耽误了则么办?因为javascript线程与UI线程互斥,某些任务导致 javascript引擎 坑了队友,自然而然没法在16ms的节点上到达这一步,从secrets of javascript ninja中了解到,一般会摒弃这次渲染,等待下一次循环。( 如有问题请指正! )

浏览器中的 event loop 到此结束,下面说说 NodeJS 的 event loop



二 NodeJS的event loop

NodeJS 的 event loop 也是有 Macrotask queue 与 Microtask queue 的。只不过 NodeJS 的略有不同。那么主要说说不同在哪里。

NodeJS中 Macrotask queue 与 Microtask queue 实例化到API为:

Macrotask queue --> script(主程序代码),setImmediate, I/O,setTimeout, setInterval

Microtask queue --> process.nextTick, Promise

1.Macrotask queue 不同之处

上面说到了浏览器 event loop 的 Macrotask queue 在每次循环中只会读取一个任务,NodeJS 中 Macrotask queue 会一次性读取完毕( 同阶段的执行完毕,后面会说到Macrotask queue 分为 6个阶段 ),然后向下读取Microtask。

注意: 这一条与 NodeJS版本有很大关系,在看 深入浅出NodeJS 这一本书时( 看的版本很旧,不知是否有修订版,如有请告知。 ),提到的 setImmediate 每次循环只会执行一次,并且给出的示例在 v8.9.1 版本跑时已不符合书中所写。书中示例如下(+ 为自定义标记,原文中没有):
+1 process.nextTick(function () {
       console.log('nextTick执行1');
   });

+2 process.nextTick(function () {
       console.log('nextTick执行2');
   });

+3 setImmediate(function () {
       console.log('setImmediateჽ执行1');

    +4 process.nextTick(function () {
           console.log('强势插入');
       });
   });

+5 setImmediate(function () {
       console.log('setImmediateჽ执行2');
   });

+6 console.log('正常执行');

正常执行
nextTick执行1
nextTick执行2
setImmediate执行1
强势插入
setImmediateჽ执行2

在 v8.9.1 中截图如下
Similarities, differences and partial mechanisms of EventLoop between browsers and NodeJS

从图片中可以看到,至少在 v8.9.1 版本中 Macrotask queue 会直接全部执行。按照惯例从头到尾的梳理一遍

macrotask: javascript 代码,所有同步代码执行。输出:正常执行。注册 +3 +5 到 Macrotask。执行process.nextTick(),最终输出:正常执行, nextTick执行1, nextTick执行2。
  **microtask: 无
macrotask: 执行 +3 +5。 输出:setImmediate执行1, setImmediateჽ执行2。 执行process.nextTick(),最终输出:setImmediate执行1, setImmediateჽ执行2,强势插入。
microtask:

所以最终输出为:正常执行, nextTick执行1, nextTick执行2,setImmediate执行1, setImmediateჽ执行2,强势插入。


2.process.nextTick(),setImmediates,以及event loop的6个阶段

NodeJS 中 Macrotask queue会分为 6 个阶段,每个阶段的作用如下(process.nextTick()在6个阶段结束的时候都会执行):

timers:执行setTimeout() 和 setInterval()中到期的callback。

I/O callbacks:上一轮循环中有少数的I/Ocallback会被延迟到这一轮的这一阶段执行

idle, prepare:仅内部使用

poll:最为重要的阶段,执行I/O callback,在适当的条件下会阻塞在这个阶段

check:执行setImmediate的callback

close callbacks:执行close事件的callback,例如socket.on("close",func)
注:此6个阶段非笔者原创来自 https://cnodejs.org/topic/5a9...,文章从底层C代码分析NodeJS event loop。这里做只做简单整合。侵删。

在了解了这六个阶段后,我们可以发现定时器系列在NodeJS event loop中 Macrotask queue 读取顺序为:

1. setTimeout(fun,0) setInterval(fun,0) 
2. setImmediate

空口无凭,在实例中了解。的代码奉上( 代码较长,分为三段,方便阅读,避免滚动。 ):

+1 process.nextTick(function(){
    console.log("1");
});
+2 process.nextTick(function(){
    console.log("2");
    +3 setImmediate(function(){
        console.log("3");
    });
    +4 process.nextTick(function(){
        console.log("4");
    });
});

+5 setImmediate(function(){
    console.log("5");
    +6 process.nextTick(function(){
        console.log("6");
    });
    +7 setImmediate(function(){
        console.log("7");
    });
});
+8 setTimeout(e=>{
    console.log(8);
    +9 new Promise((resolve,reject)=>{
        console.log(8+"promise");
        resolve();
    }).then(e=>{
        console.log(8+"promise+then");
    })
},0)

+10 setTimeout(e=>{ console.log(9); },0)

+11 setImmediate(function(){
    console.log("10");
    +12 process.nextTick(function(){
        console.log("11");
    });
    +13 process.nextTick(function(){
        console.log("12");
    });
    +14 setImmediate(function(){
        console.log("13");
    });
});
console.log("14");

+15 new Promise((resolve,reject)=>{
    console.log(15);
    resolve();
}).then(e=>{
    console.log(16);
})

这么复杂的异步嵌套在一起是不是很头疼呢?
我!不!看!了!

Similarities, differences and partial mechanisms of EventLoop between browsers and NodeJS

The last combing, the largest and most complete combing. Since ancient timessort it out from beginning to end

macrotask: javascript code, all synchronous code execution. Output: 14. Execute process.nextTick(), final output: 14, 15, 1, 2, 4. Register 3 5 8 11 to Macrotask. Register 15 to Microtask.
microtask: Execute 15 and output 16
macrotask: Execute 8 10 and output 8, 8promise, 9. Register 9 to Microtask.
microtask: Execute 9 and output 8promise then
macrotask: Execute 5 11 3 and output 5, 10, 3. Register 7 14 to macrotask. Execute process.nextTick(), final output: 5 10 3 6 11 12.
microtask: None
macrotask: Execute 7 14. Output: 7, 13
microtask: None

The final output is: 14,15,1,2,4 ,8,8promise,9,8promise then,5,10,3,6,11,12,7,13



##三End

This is the end. The browser and NodeJS event loops have all been analyzed. In the process, some of the content of Ruan Yifeng's blog, Zhihu, and CSDN articles were cited and deleted.

Recently I have gained a lot from understanding some basic knowledge. These include for of.... and all kinds of weird questions. I'll write them down when I have time.

The above is the entire content of this article. I hope it will be helpful to everyone's study. For more related content, please pay attention to the PHP Chinese website!

Related recommendations:

Use javascript to determine the browser type

Use Node to provide static file services

JS browser event loop mechanism

The above is the detailed content of Similarities, differences and partial mechanisms of EventLoop between browsers and NodeJS. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn