nodejs异步任务,nodejs定时任务
Nodejs异步定时器滴答;第节-代码
Nodejs异步定时器滴答;计时器:
在前端开发中,我们经常使用setTimeout函数组。这组函数实际上不属于语言标准,它们只是扩展。在浏览器中,它们属于BOM(浏览器对象扩展),即它的确切定义是:window.setTimeout,与window.alert、window . open . http://code.google.com/p/doctype/wiki/WindowSetTimeoutMethod等函数处于同一级别
你可以在浏览器的控制台中监控窗口对象,或者检查chromium的实现:
http://codesearch . Google . com/# OAMlx _ jo-CK/src/third _ party/WebKit/Source/WebCore/page/DOM window . h
当然,在nodejs中,您必须实现自己的计时器。nodejs的src目录中,timer.h/cc是libev中ev_timer的浅包。在src/node.js文件中,这组函数放在全局范围内(相当于浏览器中的窗口)。startup . global time outs=function(){
global.setTimeout=function() {
var t=native module . require( timers );
return t.setTimeout.apply(this,arguments);
global.setInterval=function() {
var t=native module . require( timers );
return t.setInterval.apply(this,arguments);
.
通过我们对libev的分析,已经知道libev中队timer的添加/删除/更新操作都是O(lg(n)),
(http://cnodejs.org/blog/?的利比夫分析p=2489)
但实际上,在我们的日常开发中,以网络套接字为例,我们经常会为它设置超时,即如果这个套接字在一段时间内(120秒或60秒)没有活动事件,我们就会关闭它(这个技巧很重要,否则由于一些内部或外部的原因,很容易占用过多的描述符)。注意,我们的超时是一个统一的值。这时,我们可以采用下面的算法。
当我们在不同的时间多次调用setTimeout(fn,1000)时,我们只通过一个定时器来管理所有这些事件。这些事件放在一个双链表中,顺序自然是按时间排序的。当定时器在某个时间被触发时,它将依次从prev链表头中取出节点并检查它们。如果超时,就会被触发。
(见lib/timer _legacy.js由于libev在windows上不能很好的工作,nodejs项目组为libev和libeio做了一层封装,叫做libuv。使用传统方法的文件以_ legacy结尾,使用libuv的文件以_uv结尾,如net _ legacy.js、net _ uv.js、timers _ legacy。
当我们需要改变一个异步事件的时候,比如我们添加了超时控制的socket上发生了一个读/写事件,这个时候我们需要重置定时器,我们只需要把这个事件从链表中转移到链表的末尾。
(参见lib/net_legacy.js)
删除的时候情况也差不多,所以在这个场景中,只消耗了一个ev_timer,我们所有的异步事件都是以O(1)复杂度添加/删除/更新的。
注意在lib/timer_*中。js,超时值=0时不进行优化,
exports . exports . settimeout=function(callback,after) {
if (after=0) {
after==0时使用小写
timer=新计时器();
timer.ontimeout=回调;
否则{
}
同时,setInterval函数也没有优化。
下一个刻度:
NextTick是个很有意思的东西。它实际上是由准备观察器实现的。libev中事件循环的每次迭代在nodejs中称为“Tick”,而在libev中支持prepare watcher,每次迭代开始时触发。在src/node.cc中,我们看到类似的代码(以4.9版为例):
ev _ prepare _ init(node:prepare _ tick _ watcher,node:prepare tick);
EV _ prepare _ start(EV _ DEFAULT _ UC _ node:prepare _ tick _ watcher);
EV _ unref(EV _ DEFAULT _ UC);
函数PrepareTick中会调用一个回调函数,这个function _tickCallback在src/node.js中:
startup . process nexttick=function(){
var next tick queue=[];
过程。_tickCallback=function() {
process.nextTick=函数(回调){
nextTickQueue.push(回调);
过程。_ needTickCallback();
};
当我们调用API process.nextTick时,我们实际上是在nextTickQueue的闭包队列中添加了一个函数。下一个事件循环开始时,会自动触发prepare_tick_watcher,调用我们设置的函数。
注意上面有一个问题,就是如果没有下一个事件循环的机会,比如此时没有实质性的watcher要监控,那么事件循环就会退出。要避免这种情况,请处理。_needTickCallback()将在nextTick函数中调用一次,这将使tick_spinner(一个空闲的观察器)处于活动状态,以防止退出事件循环。
从nextTick原理我们知道,当我们添加一个挂起事件时,它的复杂度是O(1),但是如果你使用setTimeout(fn,0),就像我们在libev分析中说的,它的输入输出是O(lg(n)),所以nodejs的官方文档说:
在围绕事件循环的下一个循环中调用这个回调。这不是setTimeout(fn,0)的简单别名,要高效得多。
NextTick确实效率高很多,我骗你!
部分:
通过以上分析,我们至少可以得出两个肤浅的结论:
#1如果可能,在调用setTimeout时尝试使用相同的超时值。
#2尝试使用process.nextTick而不是setTimeout(fn,0)
标签:
原创文章
由windyrobin于2011年9月13日21: 45发表
由windyrobin在2012年1月19日11: 55重新编辑
分享到微博
回复2 #1
爱管闲事的
最后,总结有很多好处。希望爱多哥能有更多简单的文章~
持续关注~
史努比在2011年9月14日22: 15回复。
#2
宿迁
定时器多路复用。消除疑虑。以后还得深入源代码。
宿迁回复2011年9月23日00: 21。