setTimeout 在 Node.JS 中是如何工作的?
Posted
技术标签:
【中文标题】setTimeout 在 Node.JS 中是如何工作的?【英文标题】:How does setTimeout work in Node.JS? 【发布时间】:2011-08-16 11:40:48 【问题描述】:我猜一旦它被执行它就在队列中,但是在队列中是否有任何保证它会在 X 毫秒后准确调用?或者队列中其他更重的任务会延迟它吗?
【问题讨论】:
任何非实时操作系统都无法保证高度的准确性。系统上的各种事情都可能(并且将会)妨碍计时器机制。 【参考方案1】:非阻塞的想法是循环迭代很快。因此,为每个刻度进行迭代应该花费足够短的时间,以使 setTimeout 在合理的精度范围内准确(关闭可能
理论上你是对的。如果我编写一个应用程序并阻止滴答声,那么 setTimeouts 将被延迟。所以要回答你的问题,谁能保证 setTimeouts 准时执行?通过编写非阻塞代码,您可以将准确度控制到几乎任何合理的准确度。
只要 javascript 在代码执行方面是“单线程”的(不包括网络工作者等),这种情况总会发生。单线程的特性在大多数情况下是一个巨大的简化,但需要非阻塞习惯用法才能成功。
在浏览器或node中试试这段代码,你会发现不能保证准确性,相反,setTimeout会很晚:
var start = Date.now();
// expecting something close to 500
setTimeout(function() console.log(Date.now() - start); , 500);
// fiddle with the number of iterations depending on how quick your machine is
for(var i=0; i<5000000; ++i)
除非解释器优化循环(它不在 chrome 上),否则你会得到成千上万的东西。去掉环,你会看到鼻子上有 500...
【讨论】:
【参考方案2】:确保代码执行的唯一方法是将您的 setTimeout 逻辑放在不同的进程中。
使用子进程模块生成一个新的 node.js 程序,该程序执行您的逻辑并通过某种流(可能是 tcp)将数据传递给该进程。
这样,即使您的主进程中正在运行一些长阻塞代码,您的子进程也已经自己启动并在新进程和新线程中放置了一个 setTimeout,因此会在您期望的时候运行。
更复杂的情况是在硬件级别上,您运行的线程多于进程,因此上下文切换会导致(非常小的)您预期的时间延迟。这应该可以忽略不计,如果这很重要,您需要认真考虑您要尝试做什么,为什么需要如此精确,以及可以使用什么样的实时替代硬件来代替。
一般来说,使用子进程并将多个节点应用程序作为单独的进程运行,以及负载平衡器或共享数据存储(如 redis)对于扩展代码非常重要。
【讨论】:
【参考方案3】:setTimeout 的语义与 Web 浏览器中的语义大致相同:超时参数是在执行之前等待的 最小 毫秒数,而不是保证。此外,传递 0、非数字或负数将导致它等待最小毫秒数。在 Node 中,这是 1 毫秒,但在浏览器中可能高达 50 毫秒。
这样做的原因是 JavaScript 没有抢占 JavaScript。考虑这个例子:
setTimeout(function ()
console.log('boo')
, 100)
var end = Date.now() + 5000
while (Date.now() < end) ;
console.log('imma let you finish but blocking the event loop is the best bug of all TIME')
这里的流程是:
-
将超时时间安排为 100 毫秒。
忙等待 5000 毫秒。
返回事件循环。检查挂起的计时器并执行。
如果不是这种情况,那么您可以让一个 JavaScript “中断”另一个。我们必须设置互斥锁和信号量等,以防止这样的代码非常难以推理:
var a = 100;
setTimeout(function ()
a = 0;
, 0);
var b = a; // 100 or 0?
Node 的 JavaScript 执行的单线程性使其比大多数其他并发样式更易于使用。当然,权衡是程序的一个行为不端的部分可能会用无限循环阻塞整个事情。
这是一个比先发制人的复杂性更适合战斗的恶魔吗?这取决于。
【讨论】:
您会因为非常准确的console.log
消息而获得 +1。
我喜欢你在字符串中大写 TIME 的方式。很好的解释!!!【参考方案4】:
setTimeout
是一种线程,它持有一个给定时间的操作并执行。
setTimeout(function,time_in_mills);
这里的第一个参数应该是一个函数类型;例如,如果您想在 3 秒后打印您的姓名,您的代码应如下所示。
setTimeout(function()console.log('your name'),3000);
要记住的关键点是,无论您想使用setTimeout
方法做什么,都在函数内部 执行。如果你想通过解析一些参数来调用其他方法,你的代码应该如下所示:
setTimeout(function()yourOtherMethod(parameter);,3000);
【讨论】:
【参考方案5】:setTimeout(callback,t)
用于在 至少 t 毫秒后运行回调。实际延迟取决于许多外部因素,例如操作系统计时器粒度和系统负载。
所以,它有可能会在设置的时间之后被调用,但之前永远不会被调用。
计时器不能超过 24.8 天。
【讨论】:
以上是关于setTimeout 在 Node.JS 中是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章