分析Node.js中的event-loop机制(node.js,web开发)

时间:2024-05-04 12:49:17 作者 : 石家庄SEO 分类 : web开发
  • TAG :

分析Node.js中的event-loop机制

libuv

在学习event-loop之前,先了解下node的libuv。libuv负责不同操作系统上的不同I/O模型的实现,并且把不同的实现抽象为能应用与第三方应用程序的API。

分析Node.js中的event-loop机制

问题

在正式学习event-loop前,先思考一个问题

setTimeout(()=>{console.log("timer1");Promise.resolve().then(()=>{console.log("promise1");});},0);setTimeout(()=>{console.log("timer2");Promise.resolve().then(()=>{console.log("promise2");});},0);

这段代码在浏览器中运行的结果是怎样的?

分析Node.js中的event-loop机制

在node中运行的结果又是怎样的呢?

在node8.6之前:

分析Node.js中的event-loop机制

node8.6之后:

分析Node.js中的event-loop机制

为什么会有这样的结果,我们稍后分析!

nodeJs 中的event-loop

首先,来看一张图:

分析Node.js中的event-loop机制

在图中可以看到6个阶段,分别是:timers,pending callbacks,idle/prepare,poll,check,close callbacks。

  • timers阶段:主要执行setTimeOut,setInterval的回调

  • pending callbacks阶段:执行一些系统调用的错误,比如说网络通信的错误回调

  • idle/prepare阶段:只在系统内部使用(这个阶段我们控制干涉不了)

  • poll阶段:获取新的I/O事件,比如获取一个读取文件的I/O回调。在适合的情况下,nodejs将阻塞在这个阶段

  • check阶段:执行 setImmediate的回调

  • 比如执行sokect的destory,close事件回调

每一个阶段都遵循一个FIFO(先入先出)的规则来执行任务队列里面的任务。在这六个阶段中,我们着重需要关注的是timers,poll,check阶段。我们日常开发中的绝大部分异步任务都是在这三个阶段处理的。

timers

我们先来说一说timers阶段。
timers是事件循环的第一个阶段,nodejs会去检查有没有已经过期了的timer,如果有,就将它的回调放入队列中。但是nodejs并不能保证timer在预设事件到了就会立即执行回调,这是因为nodejs对timer的过期检查不一定靠谱,它会受机器上其他运行程序的影响,或者是会遇到当前主线程不空闲的情况。
对于这里的不确定性,官网上举了一个例子:
先声明一个setTimeOut,然后外部读取一个文件,当读取文件操作超过定时器的时间,这样一来读文件操作就会把定时器的回调延后,这就是前面说的主线程不空闲的情况。

poll

poll阶段主要是执行两件事情:

1、处理poll阶段的任务队列

2、当有了已经超时的timer执行它的回调函数

分析Node.js中的event-loop机制

在上图中,我们还可以看到:在poll阶段执行完poll任务队列的任务之后,会去检查有无预设的setImmediate,如果有,则进入check阶段,如果没有,则nodejs将会阻塞在这里。

这里我们就会有一个疑问了,如果阻塞在poll阶段,那我们设置的timer岂不是执行不了了吗?
其实当event-loop阻塞在poll阶段时,nodejs会有一个检查机制,它会去检查timers队列是否为空,如果不为空,则重新进入timers阶段。

check

check阶段主要时执行setImmediate的回调函数。

小总结

event-loop的每个阶段都有一个队列,当event-loop达到某个阶段之后,将执行这个阶段的任务队列,直到队列清空或者达到系统规定的最大回调限制之后,才会进入下一个阶段。当所有阶段都执行完成一次之后,称event-loop完成一个tick。

案例

上面我们说完了event-loop的理论部分,但是光有理论我们也还是不能很清晰的理解event-loop。下面我们就根据几个demo来更加深入的理解下event-loop!

demo1

constfs=require('fs')fs.readFile('test.txt',()=>{console.log('readFile')setTimeout(()=>{console.log('settimeout');},0)setImmediate(()=>{console.log('setImmediate')})})

执行结果:

分析Node.js中的event-loop机制

可见执行结果跟我们前面的分析时一致的!

demo2

constfs=require("fs");constEventEmitter=require("events").EventEmitter;letpos=0;constmessenger=newEventEmitter();messenger.on("message",function(msg){console.log(++pos+"message:"+msg);//});console.log(++pos+"first");//process.nextTick(function(){console.log(++pos+"nextTick");//});messenger.emit("message","hello!");fs.stat(__filename,function(){console.log(++pos+"stat");//});setTimeout(function(){console.log(++pos+"quicktimer");//},0);setTimeout(function(){console.log(++pos+"longtimer");//},30);setImmediate(function(){console.log(++pos+"immediate");//});console.log(++pos+"last");//

结果:

分析Node.js中的event-loop机制

了解下浏览器和node的event-loop差异在什么地方

在node 8.6 之前:

浏览器中的微任务队列会在每个宏任务执行完成之后执行,而node中的微任务会在事件循环的各个阶段之间执行,即每个阶段执行完成之后会去执行微任务队列。

在8.6之后:

浏览器和node中微任务的执行是一致的!

所以,在文章开头,我们提出的思考的问题就有了结果。

关于 process.nextTick()和setImmediate

process.nextTick()

语法:process.nextTick(callback,agrs)

执行时机:

这个函数其实是独立于 Event Loop 之外的,它有一个自己的队列,当每个阶段完成后,如果存在 nextTick 队列,就会清空队列中的所有回调函数,并且优先于其他 microtask 执行。递归的调用process.nextTick()会导致I/O starving,官方推荐使用setImmediate()

关于starving现象的说明:

constfs=require("fs");fs.readFile("test.txt",(err,msg)=>{console.log("readFile");});letindex=0;functionhandler(){if(index>=30)return;index++;console.log("nextTick"+index);process.nextTick(handler);}handler();

运行结果:

分析Node.js中的event-loop机制

可以看到,等到nextTick函数呗执行30次之后,读取文件的回调才被执行!这样的现象被称为 I/O 饥饿

当我们把 process.nextTick 换为 setImmediate

constfs=require("fs");fs.readFile("test.txt",(err,msg)=>{console.log("readFile");});letindex=0;functionhandler(){if(index>=30)return;index++;console.log("nextTick"+index);setImmediate(handler);}handler();

结果:

分析Node.js中的event-loop机制

造成这两种差异的原因是,嵌套调用的setImmediate的回调被排到了下一次event-loop中去!

event-loop核心思维导图

分析Node.js中的event-loop机制

 </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
本文:分析Node.js中的event-loop机制的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:html5中不同图片资源跨域画布污染如何解决下一篇:

8 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18