128

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

4

5 回答 5

187

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')

这里的流程是:

  1. 将超时时间安排为 100 毫秒。
  2. 忙等待 5000 毫秒。
  3. 返回事件循环。检查挂起的计时器并执行。

如果不是这种情况,那么你可以让一个 JavaScript “中断”另一个。我们必须设置互斥锁和信号量等,以防止这样的代码非常难以推理:

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

Node 的 JavaScript 执行的单线程性使其比大多数其他并发风格更易于使用。当然,权衡是程序的一个行为不端的部分可能会用无限循环阻塞整个事情。

这比抢占的复杂性更适合战斗吗?那要看。

于 2012-05-26T16:06:09.847 回答
45

非阻塞的想法是循环迭代很快。因此,为每个刻度进行迭代应该花费足够短的时间,以使 setTimeout 在合理的精度范围内准确(关闭可能 <100 毫秒左右)。

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

只要 javascript 在代码执行方面是“单线程”的(不包括 web-workers 等),这种情况总会发生。在大多数情况下,单线程性质是一个巨大的简化,但需要非阻塞习语才能成功。

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

于 2011-05-01T15:05:04.480 回答
16

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

setTimeout(function,time_in_mills);

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

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

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

setTimeout(function(){yourOtherMethod(parameter);},3000);
于 2017-01-12T15:21:55.407 回答
9

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

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

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

更复杂的情况是在硬件级别上,您运行的线程多于进程,因此上下文切换将导致(非常轻微)延迟您的预期时间。这应该可以忽略不计,如果这很重要,您需要认真考虑您要做什么,为什么需要这样的准确性以及可以使用哪种实时替代硬件来完成这项工作。

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

于 2011-05-01T16:19:05.263 回答
9

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

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

计时器不能超过 24.8 天。

于 2017-02-06T16:57:03.413 回答