setTimeout如何在Node.JS中工作?


112

我猜想它一旦执行就在队列中,但是在队列中是否有任何保证会在X毫秒后准确调用?还是队列中其他繁重的任务会延迟它?


14
任何非实时操作系统都不可能保证具有很高的准确性。系统上的各种事物都可以(并且将)妨碍计时器机制。
尖尖的

Answers:


174

setTimeout的语义与Web浏览器中的语义大致相同:超时arg是执行之前等待的最小毫秒数,而不是保证。此外,传递0,非数字或负数将使它等待最小ms数。在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')

这里的流程是:

  1. 安排100ms的超时时间。
  2. 繁忙等待5000毫秒。
  3. 返回事件循环。检查未决的计时器并执行。

如果不是这种情况,那么您可能会有一点JavaScript“中断”另一部分。我们必须设置互斥体和信号量等,以防止此类代码极难推理:

var a = 100;
setTimeout(function () {
  a = 0;
}, 0);
var b = a; // 100 or 0?

Node的JavaScript执行具有单线程性,因此与大多数其他并发样式相比,它的使用要简单得多。当然,要权衡的是程序行为不当的部分可能会无限循环地阻塞整个过程。

这比抢占的复杂性更好吗?那要看。


20
您会获得+1的准确console.log信息。
基金莫妮卡的诉讼

我喜欢您在字符串中将TIME大写的方式。很好的解释!!!
Alisson

43

非阻塞的想法是循环迭代很快。因此,对每个刻度进行迭代应花费足够短的时间,以使setTimeout可以精确到合理的精度(可能小于100毫秒左右)。

从理论上讲,虽然你是对的。如果我编写了一个应用程序并阻止了滴答声,那么setTimeouts将被延迟。因此,要回答您的问题,谁可以确保setTimeouts按时执行?您可以通过编写非阻塞代码来控制精度,几乎可以达到任何合理的精度。

只要javascript在代码执行方面是“单线程的”(不包括网络工作者等),这种情况就总是会发生。在大多数情况下,单线程性质是一个巨大的简化,但是要求非阻塞用法才能成功。

在您的浏览器或节点中尝试该代码,您将发现无法保证准确性,相反,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 ...


9

确保执行代码的唯一方法是将setTimeout逻辑置于不同的进程中。

使用子进程模块生成一个新的node.js程序,该程序执行逻辑并将数据通过某种流(也许是tcp)传递给该进程。

这样,即使在您的主进程中正在运行一些长时间阻塞的代码,您的子进程也已经自行启动,并将setTimeout放置在新进程和新线程中,因此将在您期望的时间运行。

进一步的复杂性是在硬件级别上,在该级别上,您正在运行的线程数量要多于进程数量,因此上下文切换将导致(非常小的)预期时间延迟。这应该可以忽略不计,并且如果有问题,您需要认真考虑您的尝试,为什么需要这种准确性以及可以使用哪种实时替代硬件来代替这项工作。

通常,使用子进程并将多个节点应用程序作为单独的进程与负载平衡器或共享数据存储(例如redis)一起运行对于扩展代码很重要。


9

setTimeout是一种Thread,它在给定的时间内保存一个操作并执行。

setTimeout(function,time_in_mills);

在这里,第一个参数应该是函数类型;例如,如果您想在3秒后打印您的姓名,则您的代码应如下所示。

setTimeout(function(){console.log('your name')},3000);

要记住的关键是,无论您想使用该setTimeout方法做什么,都可以在一个函数中完成。如果要通过解析一些参数来调用其他方法,则代码应如下所示:

setTimeout(function(){yourOtherMethod(parameter);},3000);

8

setTimeout(callback,t)用于至少在t毫秒后运行回调。实际延迟取决于许多外部因素,例如OS计时器粒度和系统负载。

因此,有可能在设置的时间之后稍稍调用它,但永远不会在之前调用它。

计时器的时间不能超过24.8天。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.