在 Node.JS 中 setTimeout 是如何工作的?

我猜测一旦它被执行,它就在队列中,但是在队列中是否有任何保证它会在 X 毫秒之后准确地调用?或者队列上其他较高的繁重任务会延迟它?

271639 次浏览

非阻塞的思想是循环迭代很快。因此,对每个刻度进行迭代所需的时间应该足够短,以便 setTimeout 精确到合理的精度范围内(可能小于100毫秒左右)。

虽然理论上你是对的。如果我编写一个应用程序并阻止勾号,那么 setTimeout 将被延迟。那么,为了回答您的问题,谁能保证 setTimeout 按时执行?通过编写非阻塞代码,您可以控制几乎任何合理的准确度。

只要 javascript 在代码执行方面是“单线程”的(不包括 web 工作者之类的) ,这种情况就会一直发生。在大多数情况下,单线程的特性是一个巨大的简化,但是需要非阻塞习惯用法才能成功。

在浏览器或节点中尝试这段代码,您会发现不能保证准确性,相反,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..。

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

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

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

进一步的复杂性是在硬件层面上,您运行的线程比进程多,因此上下文切换将导致(非常小的)预期计时延迟。这应该是可以忽略不计的,如果它重要,您需要认真考虑您尝试做什么,为什么您需要这样的准确性,以及什么样的实时替代硬件可用来做这项工作。

通常,使用子进程并将多个节点应用程序作为单独的进程运行,同时使用负载均衡器或共享数据存储(如 redis)对于缩放代码非常重要。

SetTimeout 的语义与 Web 浏览器中的语义大致相同: timeout 参数是执行前要等待的毫秒数 最低限度,而不是保证。此外,传递0,非数字,或负数字,将导致它等待一个最小数量的 ms 在节点,这是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. 安排100毫秒的超时时间。
  2. 忙碌地等待5000毫秒。
  3. 返回到事件循环。检查挂起的计时器并执行。

如果情况并非如此,那么您可能会有一点 JavaScript“中断”另一点。我们必须设置互斥对象和信号量之类的,以防止这样的代码变得非常难以理解:

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

Node 的 JavaScript 执行是单线程的,这使得使用它比使用大多数其他并发样式更加简单。当然,折衷的方法是程序中行为不良的部分有可能用无限循环阻塞整个程序。

这个恶魔比先发制人的复杂性更适合战斗吗? 那要看情况了。

setTimeout线头的一种,它在给定的时间内保持一个操作并执行。

setTimeout(function,time_in_mills);

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

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

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

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

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

所以,有可能在设定的时间之后稍微调用它,但在此之前不会调用它。

计时器不能超过24.8天。