如何在node.js中实现setTimeout

我想知道是否有人知道如何在node.js中实现setTimeout 。 我相信我已经在某个地方看到这不是V8的一部分。 我很快试图find实现,但无法find源(BIG)。我例如发现这个timers.js文件,然后例如链接到timer_wrap.cc 。 但是这些文件并没有完全回答我所有的问题。

  • V8是否有setTimeout实现? 我想也是从源头上得出的答案是否定的。
  • setTimeout是如何实现的? JavaScript或本地或两者的组合? 从timers.js我假设沿着这两条线:

     var Timer = process.binding('timer_wrap').Timer;` 
  • 当添加多个定时器(setTimeout)时,node.js如何知道首先执行哪个? 它是否将所有定时器添加到一个集合(sorting)? 如果它被sorting,那么find需要执行的超时是O(1)和O(log n)来插入? 但是再次在timers.js我看到他们使用链表?

  • 但是再增加很多定时器根本不是问题?
  • 当执行这个脚本时:

     var x = new Array(1000), len = x.length; /** * Returns a random integer between min and max * Using Math.round() will give you a non-uniform distribution! */ function getRandomInt (min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } var y = 0; for (var i = 0; i < len; i++) { var randomTimeout = getRandomInt(1000, 10000); console.log(i + ', ' + randomTimeout + ', ' + ++y); setTimeout(function () { console.log(arguments); }, randomTimeout, randomTimeout, y); } 

    你有一点CPU使用率,但不是那么多?

  • 我想知道,如果我将得到更好的performance,我是否将所有这些callback在sorting列表中逐一执行?

你已经完成了大部分工作。 V8不提供setTimeout的实现,因为它不是ECMAScript的一部分。 你使用的函数是在timers.js中实现的,它创build了一个Timeout对象的实例,它是一个C类的包装器。

有消息人士评论说,他们如何pipe理定时器。

 // Because often many sockets will have the same idle timeout we will not // use one timeout watcher per item. It is too much overhead. Instead // we'll use a single watcher for all sockets with the same timeout value // and a linked list. This technique is described in the libev manual: // http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeouts 

这表明它正在使用链接文章中#4的双链表。

如果没有一个请求,但数千(百万…),所有采用某种types的超时具有相同的超时值,那么可以做得更好:

在开始超时时,计算超时值并将超时放在列表的末尾。

然后使用ev_timer在列表开始处的超时预期触发时触发(例如,使用技术#3)。

当有一些活动时,从列表中删除计时器,重新计算超时,再次将其附加到列表的末尾,并确保如果ev_timer从列表的开始处被更新。

这样,可以在O(1)时间内pipe理无限次数的超时,用于启动,停止和更新定时器,代价是主要的复杂性,并且必须使用恒定的超时。 常量超时确保列表保持sorting。

Node.js是围绕asynchronous操作devise的, setTimeout是其中重要的一部分。 我不会试图变得棘手,只是使用他们提供的。 相信这是足够快的,直到你certificate在你的具体情况下,这是一个瓶颈。 不要卡在过早的优化。

UPDATE

会发生什么,你基本上有一个顶级超时字典,所以所有100ms超时组合在一起。 无论何时添加新的超时或最早的超时触发器,它都会附加到列表中。 这意味着最早的超时,即最早触发的超时,就在列表的开头。 这个列表有一个计时器,它是根据直到列表中第一个项目被设置为过期的时间来设置的。

如果每次调用setTimeout 1000次具有相同的超时值,则它们将按照您调用setTimeout的顺序附加到列表中,并且不需要进行sorting。 这是一个非常有效的设置。

没有问题,许多定时器! 当uv循环调用轮询时,它将所有定时器的最接近的定时器的timeoutparameter passing给它。

[所有计时器的最近计时器]
https://github.com/joyent/node/blob/master/deps/uv/src/unix/timer.c#120

 RB_MIN(uv__timers, &loop->timer_handles) 

[通过超时参数轮询API]
https://github.com/joyent/node/blob/master/deps/uv/src/unix/core.c#276

 timeout = 0; if ((mode & UV_RUN_NOWAIT) == 0) timeout = uv_backend_timeout(loop); uv__io_poll(loop, timeout); 

注意:在Windows操作系统上,这几乎是相同的逻辑