NodeJs的Event Loop
我们之前谈过浏览器的Event Loop:https://www.cnblogs.com/amiezhang/p/11349450.html
简单来说,就是每执行一个宏任务,就去执行微任务队列,直到清空,再执行下个宏任务。
那么NodeJs的Event Loop是怎么样的呢?
NodeJS的Event Loop
NodeJs的Event Loop其实也分了宏任务和微任务
不同的是,不是每执行一个宏任务就回去清空一次微任务队列,可能是连着执行好几个才去清空一次微任务队列。
例子:
setTimeout(()=>{
console.log(1);
new Promise(resolve=>resolve()).then(()=>console.log(3))
})
setTimeout(()=>{console.log(2)})
同一个例子,在浏览器和Node环境下,输出不一致。
浏览器:

NodeJs:

可以看出:
浏览器在执行第一个 setTimeout 宏任务时,往微任务队列推了一个 console.log(3 ),所以在执行完第一个setTimeout后,浏览器的 Event Loop 机制马上去执行微任务队列,然后再执行下个setTimeout宏任务
NodeJs 在执行第一个 setTimeout 宏任务时,也往微任务队列推了一个 console.log(3),但是NodeJs的 Event Loop 机制却继续去执行下个 setTimeout 宏任务,等2个 setTimeout 宏任务都执行完后,才去执行微任务队列。
宏任务和微任务的执行机制图
图中 Tick 就是执行清空微任务的时机,我们可以看到 NodeJs 的 Event Loop 分为 5 个阶段,在每个阶段间会执行清空一次微任务队列。
所以不难理解,在上面的例子,Times 阶段有 2 个宏任务,所以,console.log(2) 需要等整个 Times 阶段走完,才能得以执行。

NodeJs 各个执行阶段
推荐阅读:https://segmentfault.com/a/1190000013102056
timers
一个timer指定一个下限时间而不是准确时间,在达到这个下限时间后执行回调。在指定的时间过后,timers会尽早的执行回调,但是系统调度或者其他回调的执行可能会延迟它们。
从技术上来说,
poll阶段控制timers什么时候执行,而执行的具体位置在timers。
下限的时间有一个范围:[1, 2147483647],如果设定的时间不在这个范围,将被设置为1。
I/O callbacks
这个阶段执行一些系统操作的回调,比如说TCP连接发生错误。
idle, prepare
系统内部的一些调用。
poll
这是最复杂的一个阶段。
poll阶段有两个主要的功能:一是执行下限时间已经达到的timers的回调,一是处理poll队列里的事件。
注:Node很多API都是基于事件订阅完成的,这些API的回调应该都在poll阶段完成。
笔者把官网陈述的情况以不同的条件分解,更加的清楚。(如果有误,师请改正。)
当事件循环进入poll阶段:
poll队列不为空的时候,事件循环肯定是先遍历队列并同步执行回调,直到队列清空或执行回调数达到系统上限。poll队列为空的时候,这里有两种情况。- 如果代码已经被
setImmediate()设定了回调,那么事件循环直接结束poll阶段进入check阶段来执行check队列里的回调。 如果代码没有被设定
setImmediate()设定回调:- 如果有被设定的
timers,那么此时事件循环会检查timers,如果有一个或多个timers下限时间已经到达,那么事件循环将绕回timers阶段,并执行timers的有效回调队列。 - 如果没有被设定
timers,这个时候事件循环是阻塞在poll阶段等待回调被加入poll队列。
- 如果有被设定的
- 如果代码已经被
check
这个阶段允许在poll阶段结束后立即执行回调。如果poll阶段空闲,并且有被setImmediate()设定的回调,那么事件循环直接跳到check执行而不是阻塞在poll阶段等待回调被加入。
setImmediate()实际上是一个特殊的timer,跑在事件循环中的一个独立的阶段。它使用libuv的API来设定在poll阶段结束后立即执行回调。
注:setImmediate()具有最高优先级,只要poll队列为空,代码被setImmediate(),无论是否有timers达到下限时间,setImmediate()的代码都先执行。
close callbacks
如果一个socket或handle被突然关掉(比如socket.destroy()),close事件将在这个阶段被触发,否则将通过process.nextTick()触发。
关于setTimeout和setImmediate
网上很多人说,这样的代码,会有随机性(自己尝试一直setTimeout先执行,偶现一次setImmediate先执行):
setTimeout(() => {
console.log('setTimeout');
}, 0); // 当 delay 设置为 0 的时候,NodeJs 会设置为 1ms,因为最小是 1ms
setImmediate(() => {
console.log('setImmediate');
})

为什么呢?
我们在上面看到在 poll 阶段,如果执行队列为空,它就会去询问 timers 那是否已经有“准备好“(例如超过 delay 时限的setTimeout)的回调函数。
如果有就先到 timers 阶段去执行setTimeout;如果没有,则去询问 check 那是否有 setImmediate 注册回调,有就直接调用setImmediate。
因为 setImmediate 是不用等就可以马上注册好的,所以随机性就在 setTimeout。
因为 setTimeout 要等 1ms 才能”准备好“,所以执行到 poll 的时候,如果 setTimeout 的 delay(这里是 1ms) 过去了,那么 setTimeout 会先执行。
如果执行到 poll 的时候,1ms 都还没过去,那么 setTimeout 就相当于没有准备好, 这样 setImmediate 就先执行了。
NodeJs的Event Loop的更多相关文章
- 理解Nodejs的Event Loop
Node的“event loop”主要是用来处理高输出量的.这很神奇,这也是为什么node可以在单线程的情况下同时处理很多的后台操作.本文就会集中讲述event loop是怎么运行的,这样你可以可以使 ...
- 【Node.js】Event Loop执行顺序详解
本文基于node 0.10.22版本 关于EventLoop是什么,请看阮老师写的什么是EventLoop 本文讲述的是EventLoop中的执行顺序(着重讲setImmediate, setTime ...
- 关于event loop
之前写了篇文章 JS运行机制,里面对event loop简单的说明,面试时又遇到了关于该知识点的题目(主要是process.nextTick和setImmediate的执行顺序不太知道,查了之后才知道 ...
- [转]Event loop——浏览器和Node区别
最近对Event loop比较感兴趣,所以了解了一下.但是发现整个Event loop尽管有很多篇文章,但是没有一篇可以看完就对它所有内容都了解的文章.大部分的文章都只阐述了浏览器或者Node二者之一 ...
- Event Loop事件循环,GET!
JS中比较让人头疼的问题之一要算异步事件了,比如我们经常要等后台返回数据后进行dom操作,又比如我们要设置一个定时器完成特定的要求.在这些同步与异步事件里,异步事件肯定是在同步事件之后的,但是异步事件 ...
- [NodeJs系列][译]理解NodeJs中的Event Loop、Timers以及process.nextTick()
译者注: 为什么要翻译?其实在翻译这篇文章前,笔者有Google了一下中文翻译,看的不是很明白,所以才有自己翻译的打算,当然能力有限,文中或有错漏,欢迎指正. 文末会有几个小问题,大家不妨一起思考一下 ...
- 不要在nodejs中阻塞event loop
目录 简介 event loop和worker pool event loop和worker pool中的queue 阻塞event loop event loop的时间复杂度 Event Loop中 ...
- nodejs(五)同步异步--BLOCKING THE EVENT LOOP
1.BLOCKING THE EVENT LOOP Node and JavaScript runtimes in general are single-threaded event loops. O ...
- 对Node.JS的事件轮询(Event Loop)的理解
title: Node.JS的事件轮询(event loop)的理解 categories: 理解 tags: Node JS 机制 当我们知道I/O操作和创建新线程的开销是巨大的! 网站延迟的开销 ...
随机推荐
- 使用UltraISO制作Centos7 U盘启动盘遇到的坑
下载.安装UltraISO软件 安装好以后,打开软件 击菜单栏的"文件"选项,再点击"打开"按钮,选择要刻录的系统镜像 点击菜单栏的"启动" ...
- 滤波器算法(1)-卡尔曼滤波小车附带题目与MATLAB程序
1 简介 由卡尔曼这个学者提出的最佳线性滤波器,单纯时域维度即可实现[无需进行频域变换] 2 思路 由上一时刻的最佳估计值XKE_P预测①当前时刻预测值Pxv 与 ②当前时刻的测量值Mxv 进行联立计 ...
- jQueryUI的widget的Hello World
为了看懂jQuery-File-Upload里面的代码,所以学习到这里 //main.js //实践自定义jquery widget,风格1 (function($){ //$.widget('命名空 ...
- bootstrap的tree使用
效果图: 先引用,顺序很重要 <script src="~/Content/bootstrap-table/bootstrap-table.min.js"></s ...
- ef报错(因为相同类型的其他实体已具有相同的主键值)
修改数据的时候查询了下数据 于是报错了,我吧查询不加入缓存中,吧getbyid的方法改成.AsNoTracking().FirstOrDefault(p => p.x== x)
- Go 互斥锁(sync.Mutex)和 读写锁(sync.RWMutex)
什么时候需要用到锁? 当程序中就一个线程的时候,是不需要加锁的,但是通常实际的代码不会只是单线程,所以这个时候就需要用到锁了,那么关于锁的使用场景主要涉及到哪些呢? 多个线程在读相同的数据时 多个线程 ...
- vue实现一个评论列表
<!DOCTYPE html> <html> <head> <title>简易评论列表</title> <meta charset=& ...
- CSS重排和重绘
一.什么是重绘Repaint和重排 (回流 reflow) 重绘:当元素的一部分属性发生改变,如外观.背景.颜色等不会引起布局变化,只需要浏览器根据元素的新属性重新绘制 ,使元素呈现新的外观叫做重绘. ...
- (一)react-native开发系列之Mac开发环境配置
写在前面 在开始之前,先说下选择react-native的背景原因 最近一年来,公司为了节省开发成本,以及降低维护成本,指派我开始做起前端开发app的工作,我和公司的小伙伴们就开始了漫长的app开发之 ...
- 【MySql】牛客SQL刷题(上)
牛客SQL题目 题目链接:https://www.nowcoder.com/ta/sql 查找最晚入职员工的所有信息 select * from employees where hire_date = ...