【问题标题】:node.js setTimeout resolution is very low and unstablenode.js setTimeout 分辨率很低且不稳定
【发布时间】:2013-08-19 15:37:28
【问题描述】:

我遇到了解决 setTimeout 的问题。当我将它设置为 50 毫秒时,它从 51 到甚至 80 毫秒不等。当我使用睡眠模块时,我能够获得 50 µs 的分辨率,那么 setTimeout 函数至少获得 1 ms 的问题是什么?有没有办法解决/避免这种情况? sleep 的问题在于,即使应该触发回调函数,它也会延迟一切,它会等待......是否有替代解决方案可以在恰好 50 毫秒的延迟中触发某些事件?

以睡眠模块为例:

var start = new Date().getTime();
sleep.usleep(50);
console.log(new Date().getTime() - start);`

结果是:0。而 microtime 表示它是 51 到 57 µs。那是什么鬼?

【问题讨论】:

  • 您可能会对 John Resig 的“How JavaScript Timers Work”感兴趣。
  • 但是,即使setTimeout中的函数只是写new Date().getTime()它仍然是完全不可靠的!
  • 这篇文章解释了可靠性,这取决于事件循环空闲的频率。如果它正忙于另一个任务,则计时器必须等待该任务完成。 timeout 值只是一个最小值,不是保证。
  • 有没有办法让它更可靠?或任何其他解决方法?如果不使用 100% 的处理器,睡眠会很好。
  • 之所以说 51 到 57 微秒是因为 V8 引擎处理下一行脚本需要这么长时间(1 到 7 微秒)。仅仅因为这对您来说是一行代码。 usleep 函数可能会进行一些系统调用,V8 引擎需要一段时间才能从这些调用中恢复过来并继续运行您的脚本。更何况,获取当前时间本身需要系统调用本身,这也需要时间。

标签: node.js settimeout resolution


【解决方案1】:

来自setTimeout docs

请务必注意,您的回调可能不会在精确的延迟毫秒内被调用

延迟的精度取决于您的代码块有多少,这意味着如果您在代码具有的单个线程上执行大量操作,setTimeout 可能会被触发很多延迟。相反,它几乎是准确的。

您可以自己看到差异,执行此操作

var start = Date.now();
setTimeout(function() { console.log(Date.now() - start); }, 500);
for(var i=0; i<999999999; ++i){}

// 1237ms

并注意与此的区别:

var start = Date.now();
setTimeout(function() { console.log(Date.now() - start); }, 500);

// 507ms

【讨论】:

  • 第二个例子仍然是 507 毫秒!!!试试...var start = Date.now();sleep.usleep(50); console.log(Date.now() - start);`它会给你0零!微时间显示57 microseconds
  • 我认为这是一个设计“限制”。
  • Calling setTimeout will add a message to the queue after the time passed as second argument. If there is no other message in the queue, the message is processed right away; however, if there are messages, the setTimeout message will have to wait for other messages to be processed. For that reason the second argument indicates a minimum time and not a guaranteed time. -Mozilla docs
  • 我刚刚在 Firefox 控制台(ubuntu 16.04 LTS)中尝试了该示例,并且非常惊讶地看到在 499501 之间打印,甚至比“最短时间”还短!我想这在实际应用中是可以的,甚至可能是一个四舍五入的499.4... ms,但我觉得这很了不起。在 node.js 中,它主要是 501 ms,有时是 500 ms
【解决方案2】:

因此,为了让 setTimeout 之类的事情尽可能获得最佳时间速率,我们希望让给定模块的事件循环尽可能小。这最大限度地减少了 V8 引擎在循环中花费的时间。最佳情况:

setInterval(function(){}, 50);

如果这是文件中唯一的东西,那么精度会非常高,因为没有其他事情发生。所以,我们所做的是,我们为特定模块创建一个进程,该进程只执行我们希望在此时间间隔内完成的工作。如果函数中唯一的东西是异步请求,它和上面一样好。所以我们在 setInterval 函数上获得了非常高的精度。

在一个文件中,我们想要以下内容,我们将这个文件称为 file1.js。

var http = require('http');

var options = {
    hostname: 'localhost',
    port: 8888,
    path:'/',
    method: 'GET'
}

setInterval(function(){
    console.log('File:' + (new Date).getTime() % 50);
    //The Modulus of the time in MS should change by no more than 1 MS on consecutive runs, I confirmed this works just dandy on my system(though the first couple intervals were slower in some instances, worse I saw was a 3 MS delay)
    var req = http.request(options, function (res) {
        res.on('data', function (data) {
            console.log(data.toString());
        });
    });

    req.end();
}, 50);

在第二个文件中,我们可以做更多的工作,无论我们实际想要什么。重要的是我们生成了一个进程来完成上述工作,包含在它的 OWN PROCESS 中。这允许该进程 V8 处理器保持这个事件循环非常小。我们仍然是 CPU 上给定进程的操作系统管理的受害者,但我们仍然可以期望我们的 file1.js 模块至少每毫秒得到一次关注,因为它所做的只是一个异步调用,然后是寻找下次它需要启动所说的异步调用时,我们在调用之间得到的最大延迟是 1 毫秒(至少这是我在系统上看到的全部)。因此,另一个文件可以包含任意数量的工作,并且有一个非常重要的行:

file2.js:

var http = require('http');
var child_process = require('child_process');


http.createServer(function (req, res) {
    res.write('Hi There');
    res.end();
}).listen(8888);

///Do whatever else you need to do

child_process.fork('file1.js');//Add in this line!!!!

预期输出:

File:29
Hi There
File:29
Hi There
File:30//Notice these values only increase by 0 or 1.  
Hi There
File:31
Hi There
File:31
Hi There
File:32
Hi There
File:34//Uh oh, one went up by 2... not terrible
Hi There
File:35
Hi There
File:35

【讨论】:

  • 那么没有办法提高“寻找回调”函数的效率吗?
  • 寻找回调?
  • 看看我上面的例子。有可能的。这个功能只是搞砸了。
  • 正如我所说,如果您愿意忙于等待,您可以获得更高的精度(可能会降低到舍入会让您相信它是完美的),但为了所有编程之神存在时,您的处理器、散热器等...不要这样做。
  • 好的,但是大约 10 毫秒的分辨率和大约 10 微秒之间存在巨大差异!
【解决方案3】:

是的,我希望它更精确,并且我有趣的 waitSort 可以工作:)

但是它在这里工作但在节点中不起作用

const posInts = [5,2,7,9,4,6,1,3]

const waitSort = arr => new Promise(done => {
  const ret = []
  const last = Math.max(...arr)
  arr.forEach(i => {
    setTimeout(() => {
      ret.push(i)
      if(i === last){
        done(ret)
      }
    }, i)
  })
})

waitSort(posInts).then(console.log)
console.log(posInts)

【讨论】:

    猜你喜欢
    • 2014-03-31
    • 1970-01-01
    • 2012-12-11
    • 1970-01-01
    • 2020-11-12
    • 2015-04-26
    • 1970-01-01
    • 2011-04-07
    • 1970-01-01
    相关资源
    最近更新 更多