欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  web前端

一文带你了解Node.js中的eventloop

程序员文章站 2022-02-09 08:46:28
...
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。下面本篇文章就来带大家掌握Node.js中的eventloop,希望对大家有所帮助!

一文带你了解Node.js中的eventloop

其实在前面的文章我也讲述过浏览器中的eventloop。然而在NodeJs中的eventloop与浏览器的是有区别的。对于写nodejs的人来说掌握eventloop是一项很重要的技能。因为这意味着你不仅是会写js,而对NodeJs也是有研究的。

为什么要有eventloop?

我们知道NodeJs的本质是把浏览器的v8搬到了操作系统中运行,因此也把浏览器的事件循环拿过来了。可是为什么会出现eventloop这样的设计呢?

从历史原因上来看,js在设计时只是一门很简单的为了在页面上操作一下dom的语言(相信大家都听过js只用了10天就设计出来的故事)。出于这个目标,我们当然希望js的运行尽可能的简单,轻量,有多轻呢?轻到js的渲染引擎是在一个线程中运行的。

那么问题来了如果是在一个线程上运行js,当代码是线性的时候,当然是没有问题的。但在页面上,我们需要用户的交互,而这些交互是不知道为什么时候发生的。那js要怎么处理?如果眼前有正在运行的代码,一个用户交互进来之后,程序该怎么反应?如果先处理用户的交互,那原来的程序就会被暂停(也就是阻塞)。为了避免这种阻塞,js采用了一个办法,就是用一个消息队列,来存放这种用户交互。等所有的程序跑完之后,再去消息队列中拿交互事件,然后执行。这样就解决了阻塞的问题了。

浏览器的eventloop

我们都知道浏览器在浏览页面的时候,用户交互是随时可能发生的,为了可以即时响应用户。js是不会关闭的,他会不停的循环。大致如下:

向消息队列拿任务-->执行任务-->执行完毕--> 向消息队列拿任务--> ....

当然我们在之前的事件循环文章中讲过,为了给不同的异步任务分类,在事件循环中其实是有宏任务和微任务的区分的。他们的执行大致为

向消息队列拿微任务-->执行微任务-->微任务执行完毕--> 向消息队列拿宏任务-->执行宏任务-->宏任务执行完毕-->向消息队列拿微任务-->...

NodeJs的eventloop

node的事件循环其实大致思路跟在浏览器上的是相似的,但nodeJs对不同的宏任务又作出了不同时期的区分。下面是官方的流程图:

一文带你了解Node.js中的eventloop

可以看到nodeJs中每次事件循环被分成了具体的6个时期,每个时期会用指定的宏任务。然后在每个时期的宏任务执行之前,会优先执行完微任务队列。

总览

timers 执行由setTimeout()setInterval() 触发的回调
pending callbacks 执行延迟到下一个循环迭代的I / O回调
idle, prepare 只在内部使用,开发者可以不关注
poll 检索新的I / O事件;执行I / O相关的回调(会执行几乎所有的回调,除了 close callbacks 以及 timers 调度的回调和 setImmediate() 调度的回调,在恰当的时机将会阻塞在此阶段)
check 执行setImmediate()
close callbacks 比如socket.on('close', ...)

其实通过上述表格,我们已经很清晰知道整个事件循环机制的执行顺序了。但可能大家还会有一些疑问。下面来详细讲一下。

pending callbacks

这个阶段其实是处理由于操作系统出错,导致一些本应在上次事件循环中执行的回调。例如一些TCP错误。因此这部分,开发者不能主动操作,是NodeJs的一些容错机制。

check

同样的,setImmediate是nodejs特有的api,他可以立即创建一个异步宏任务。不仅如此,nodejs在事件循环中还专门设了一个check时期,在这个时期会专门执行setImmediate的回调。甚至你可以在这个时期中如果不停的产生setImmediate回调,eventloop会优先处理。

close callbacks

这个时期处理关闭事件,如socket.on('close', ...) 等这样可以确保在一些通讯结束前,所有任务都完成了。

微任务在eventloop中

我们先来回顾浏览器与nodejs的差异:

宏任务:

任务 浏览器 Node
I/O
setTimeout
setInterval
setImmediate
requestAnimationFrame

微任务:

任务 浏览器 Node
process.nextTick
MutationObserver
Promise.then catch finally

可以看到process.nextTick是nodejs特有的微任务,不仅如此,process.nextTick()的优先级高于所有的微任务,每一次清空微任务列表的时候,都是先执行 process.nextTick()

执行差异

不仅是任务类型上有差异,在执行上2个环境其实也有差异。在浏览器上执行任务的时候,每执行一个宏任务之前,需要先确保微任务队列执行完了。而在nodejs上是每个时期之前,先确保微任务队列执行完。也就是说在假如在timer时期,会先把所有setTimeout,setInterval的宏任务执行完。在执行完微任务,再进入下个时期。

注意:以上执行规则是在nodejs的v11版本之前的规则。在11版本之后nodejs的执行输出是跟浏览器一样的。

setImmediate() vs setTimeout()

setImmediate() 和 setTimeout()的执行先后顺序是不一定的,就是说如果你不停地执行以下代码,每次得到的结果可能是不一样的。

setTimeout(() => {
  console.log('timeout');
}, 0);

setImmediate(() => {
  console.log('immediate');
});

其中的原因是程序对时间的处理是有误差的。在setTimeout方法中设置的时间,不一定是准确的。同时在回调触发时,也无法确认事件循环处在哪个时期,可能是timer,也可能是check。所有会有不同的结果。

总结

eventloop是js运行机制里的重点内容,对于NodeJs来说,eventloop的操作空间则更大。因为它被细分为不同的时期,从而让我们可能把逻辑进一步细化。同时利用nextTick的最高优先级,可以写出在浏览器无法实现的代码。因此对于深入NodeJs的开发者来说,eventloop往往是他们考察新人对NodeJs理解的第一步。

更多node相关知识,请访问:nodejs 教程!!

以上就是一文带你了解Node.js中的eventloop的详细内容,更多请关注其它相关文章!

相关标签: Node.js eventloop