深入浅析nodejs中的事件和事件循环
跟大家讲解下有关深入浅析nodejs中的事件和事件循环,相信小伙伴们对这个话题应该也很关注吧,现在就为小伙伴们说说深入浅析nodejs中的事件和事件循环,小编也收集到了有关深入浅析nodejs中的事件和事件循环的相关资料,希望大家看到了会喜欢。
本篇文章给大家讲解一下nodejs中的event,并探讨一下setTimeout,setImmediate和process.nextTick的区别。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。nodejs中的事件循环虽然nodejs是单线程的,但是nodejs可以将操作委托给系统内核,系统内核在后台处理这些任务,当任务完成之后,通知nodejs,从而触发nodejs中的callback方法。
这些callback会被加入轮循队列中,最终被执行。
通过这样的event loop设计,nodejs最终可以实现非阻塞的IO。【相关推荐:《nodejs 教程》】
nodejs中的event loop被分成了一个个的phase,下图列出了各个phase的执行顺序:
每个phase都会维护一个callback queue,这是一个FIFO的队列。
当进入一个phase之后,首先会去执行该phase的任务,然后去执行属于该phase的callback任务。
当这个callback队列中的任务全部都被执行完毕或达到了最大的callback执行次数之后,就会进入下一个phase。
注意, windows和linux的具体实现有稍许不同,这里我们只关注最重要的几个phase。
问题:phase的执行过程中,为什么要限制最大的callback执行次数呢?
回答:在极端情况下,某个phase可能会需要执行大量的callback,如果执行这些callback花费了太多的时间,那么将会阻塞nodejs的运行,所以我们设置callback执行的次数限制,以避免nodejs的长时间block。
phase详解上面的图中,我们列出了6个phase,接下来我们将会一一的进行解释。
timers
timers的中文意思是定时器,也就是说在给定的时间或者时间间隔去执行某个callback函数。
通常的timers函数有这样两种:setTimeout和setInterval。
一般来说这些callback函数会在到期之后尽可能的执行,但是会受到其他callback执行的影响。 我们来看一个例子:
const fs = require('fs');function someAsyncOperation(callback) { // Assume this takes 95ms to complete fs.readFile('/path/to/file', callback);}const timeoutScheduled = Date.now();setTimeout(() => { const delay = Date.now() - timeoutScheduled; console.log(`${delay}ms have passed since I was scheduled`);}, 100);// do someAsyncOperation which takes 95 ms to completesomeAsyncOperation(() => { const startCallback = Date.now(); // do something that will take 10ms... while (Date.now() - startCallback < 10) { // do nothing }});上面的例子中,我们调用了someAsyncOperation,这个函数首先回去执行readFile方法,假设这个方法耗时95ms。接着执行readFile的callback函数,这个callback会执行10ms。最后才回去执行setTimeout中的callback。
所以上面的例子中,虽然setTimeout指定要在100ms之后运行,但是实际上还要等待95 + 10 = 105 ms之后才会真正的执行。
pending callbacks
这个phase将会执行一些系统的callback操作,比如在做TCP连接的时候,TCP socket接收到了ECONNREFUSED信号,在某些liunx操作系统中将会上报这个错误,那么这个系统的callback将会放到pending callbacks中运行。
或者是需要在下一个event loop中执行的I/O callback操作。
idle, prepare
idle, prepare是内部使用的phase,这里就不过多介绍。
poll轮询
poll将会检测新的I/O事件,并执行与I / O相关的回调,注意这里的回调指的是除了关闭callback,timers,和setImmediate之外的几乎所有的callback事件。
poll主要处理两件事情:轮询I/O,并且计算block的时间,然后处理poll queue中的事件。
如果poll queue非空的话,event loop将会遍历queue中的callback,然后一个一个的同步执行,知道queue消费完毕,或者达到了callback数量的限制。
因为queue中的callback是一个一个同步执行的,所以可能会出现阻塞的情况。
如果poll queue空了,如果代码中调用了setImmediate,那么将会立马跳到下一个check phase,然后执行setImmediate中的callback。 如果没有调用setImmediate,那么会继续等待新来的callback被加入到queue中,并执行。
check
主要来执行setImmediate的callback。
setImmediate可以看做是一个运行在单独phase中的独特的timer,底层使用的libuv API来规划callbacks。
一般来说,如果在poll phase中有callback是以setImmediate的方式调用的话,会在poll queue为空的情况下,立马结束poll phase,进入check phase来执行对应的callback方法。
close callbacks
最后一个phase是处理close事件中的callbacks。 比如一个socket突然被关闭,那么将会触发一个close事件,并调用相关的callback。
setTimeout 和 setImmediate的区别setTimeout和setImmediate有什么不同呢?
从上图的phase阶段可以看出,setTimeout中的callback是在timer phase中执行的,而setImmediate是在check阶段执行的。
从语义上讲,setTimeout指的是,在给定的时间之后运行某个callback。而setImmediate是在执行完当前loop中的 I/O操作之后,立马执行。
那么这两个方法的执行顺序上有什么区别呢?
下面我们举两个例子,第一个例子中两个方法都是在主模块中运行:
setTimeout(() => { console.log('timeout'); }, 0); setImmediate(() => { console.log('immediate'); });这样运行两个方法的执行顺序是不确定,因为可能受到其他执行程序的影响。
第二个例子是在I/O模块中运行这两个方法:
const fs = require('fs'); fs.readFile(__filename, () => { setTimeout(() => { console.log('timeout'); }, 0); setImmediate(() => { console.log('immediate'); }); });你会发现,在I/O模块中,setImmediate一定会在setTimeout之前执行。
两者的共同点
setTimeout和setImmediate两者都有一个返回值,我们可以通过这个返回值,来对timer进行clear操作:
const timeoutObj = setTimeout(() => { console.log('timeout beyond time'); }, 1500); const immediateObj = setImmediate(() => { console.log('immediately executing immediate'); }); const intervalObj = setInterval(() => { console.log('interviewing the interval'); }, 500); clearTimeout(timeoutObj); clearImmediate(immediateObj); clearInterval(intervalObj);clear操作也可以clear intervalObj。
unref 和 ref
setTimeout和setInterval返回的对象都是Timeout对象。
如果这个timeout对象是最后要执行的timeout对象,那么可以使用unref方法来取消其执行,取消执行完毕,可以使用ref来恢复它的执行。
const timerObj = setTimeout(() => { console.log('will i run?'); }); timerObj.unref(); setImmediate(() => { timerObj.ref(); });注意,如果有多个timeout对象,只有最后一个timeout对象的unref方法才会生效。
process.nextTickprocess.nextTick也是一种异步API,但是它和timer是不同的。
如果我们在一个phase中调用process.nextTick,那么nextTick中的callback会在这个phase完成,进入event loop的下一个phase之前完成。
这样做就会有一个问题,如果我们在process.nextTick中进行递归调用的话,这个phase将会被阻塞,影响event loop的正常执行。
那么,为什么我们还会有process.nextTick呢?
考虑下面的一个例子:
let bar; function someAsyncApiCall(callback) { callback(); } someAsyncApiCall(() => { console.log('bar', bar); // undefined }); bar = 1;上面的例子中,我们定义了一个someAsyncApiCall方法,里面执行了传入的callback函数。
这个callback函数想要输出bar的值,但是bar的值是在someAsyncApiCall方法之后被赋值的。
这个例子最终会导致输出的bar值是undefined。
我们的本意是想让用户程序执行完毕之后,再调用callback,那么我们可以使用process.nextTick来对上面的例子进行改写:
let bar; function someAsyncApiCall(callback) { process.nextTick(callback); } someAsyncApiCall(() => { console.log('bar', bar); // 1 }); bar = 1;我们再看一个实际中使用的例子:
const server = net.createServer(() => {}).listen(8080); server.on('listening', () => {});上面的例子是最简单的nodejs创建web服务。
上面的例子有什么问题呢?listen(8000) 方法将会立马绑定8000端口。但是这个时候,server的listening事件绑定代码还没有执行。
这里实际上就用到了process.nextTick技术,从而不管我们在什么地方绑定listening事件,都可以监听到listen事件。
process.nextTick 和 setImmediate 的区别
process.nextTick 是立马在当前phase执行callback,而setImmediate是在check阶段执行callback。
所以process.nextTick要比setImmediate的执行顺序优先。
实际上,process.nextTick和setImmediate的语义应该进行互换。因为process.nextTick表示的才是immediate,而setImmediate表示的是next tick。
本文作者:flydean程序那些事
本文链接:http://www.flydean.com/nodejs-event-more/
本文来源:flydean的博客
更多编程相关知识,请访问:编程视频!!
以上就是深入浅析nodejs中的事件和事件循环的详细内容,更多请关注php中文网其它相关文章!
来源:php中文网
免责声明:本文由用户上传,与本网站立场无关。财经信息仅供读者参考,并不构成投资建议。投资者据此操作,风险自担。 如有侵权请联系删除!
-
作为A股市场中极具代表性的黄金珠宝行业龙头企业,作为中国历史最悠久的珠宝品牌之一,老凤祥(股票代码:600612)...浏览全文>>
-
宝子们,杭州 房子装修完成啦!这次要给大家分享几家设计超赞的装修公司哦。它们各具特色,从空间规划到风格...浏览全文>>
-
欲筑室者,先治其基。在上海,装修房子对于每个业主而言,都是极为关键的一步,然而,如何挑选一家值得信赖的...浏览全文>>
-
2025年以来,联通支付严格贯彻落实国家战略部署,以数字和科技为驱动,做好金融五篇大文章,履行支付为民社会...浏览全文>>
-
良工巧匠,方能筑就华居;精雕细琢,方可打造美家。当我们谈论装修公司时,选择一家靠谱可靠的公司是至关重要...浏览全文>>
-
在当今社会,随着城市化进程的高速推进,建筑垃圾的产生量与日俱增。据权威数据显示,我国每年建筑垃圾产生量超 ...浏览全文>>
-
家人们,在上海要装修,选对公司那可太重要了!古语有云:"安得广厦千万间,大庇天下寒士俱欢颜。"一个温馨的...浏览全文>>
-
近年来,新能源汽车市场发展迅猛,各大品牌纷纷推出各具特色的车型以满足消费者多样化的需求。作为国内新能源...浏览全文>>
-
近年来,随着汽车市场的不断变化和消费者需求的升级,安徽滁州地区的宝来2025新款车型在市场上引起了广泛关注...浏览全文>>
-
随着汽车市场的不断变化,滁州地区的消费者对高尔夫车型的关注度持续上升。作为大众品牌旗下的经典车型,高尔...浏览全文>>
- 安徽滁州途安L新车报价2022款,最低售价16.68万起,入手正当时
- 小鹏G7试驾,新手必知的详细步骤
- 别克GL8预约试驾,4S店的贴心服务与流程
- 安徽阜阳ID.4 CROZZ落地价全解,买车必看的省钱秘籍
- 淮北探岳多少钱 2025款落地价,最低售价17.69万起现在该入手吗?
- 安徽淮南大众CC新款价格2025款多少钱能落地?
- 淮北长安启源C798价格,最低售价12.98万起现在该入手吗?
- 安徽淮南途锐价格,各配置车型售价全解析
- 蒙迪欧试驾预约,4S店体验全攻略
- 沃尔沃XC40试驾需要注意什么
- 滁州ID.4 X新车报价2025款,各车型售价大公开,性价比爆棚
- 试驾思域,快速操作,轻松体验驾驶乐趣
- 试驾长安CS35PLUS,一键搞定,开启豪华驾驶之旅
- 天津滨海ID.6 X落地价限时特惠,最低售价25.9888万起,错过不再有
- 天津滨海凌渡多少钱?看完这篇购车攻略再做决定
- 安徽池州长安猎手K50落地价,买车前的全方位指南
- 山东济南ID.6 CROZZ 2024新款价格,最低售价19.59万起,现车充足
- 试驾海狮05EV,新手必知的详细步骤
- 生活家PHEV多少钱 2025款落地价走势,近一个月最低售价63.98万起,性价比凸显
- 奇瑞风云A9试驾,新手必知的详细步骤