使用setTimeout时,JS无法准确计算自己的执行时间

时间:2014-03-06 17:21:05

标签: javascript

请注意:链接到问题不回答我的问题。 Bergi的答案是我所要求的正确答案。

首先,请看一下这个简单的CountdownTimer:

http://jsfiddle.net/49tH7/(请参阅下面的永久性代码)

确保打开控制台,以便您可以看到倒计时。我使用最新版本的chrome进行了测试。

代码说明

请注意我们调用计时器的方式:

timer = CountdownTimer(5000, 5, function(tickNum) { console.log(tickNum) });
timer.start();

我们传递计时器应该采取的总时间倒计时(5000毫秒),它应该完成的滴答总数(在这种情况下每秒5 - 1),以及在每个滴答时调用的回调函数。回调传递了tick的编号,在这种情况下它只是打印出来。代码执行完毕后,将调用onFinish()方法,该方法只打印出计时器刚刚运行的总ms数。理论上,这应该总是非常接近传递给CountdownTimer构造函数的第一个参数,在本例中为5000。

ticks之间的时间是自适应的,考虑到执行回调所花费的时间。如果回调代码平均缓慢且复杂,optimalPauseTime()方法将检测到这一点,并开始调整滴答之间的时间,以便计时器仍然在{{1}的第一个参数中指定的总时间内完成}。因此,计时器的总运行时间和刻度总数是固定量,但刻度之间的时间是动态的和可变的。

奇怪的行为和我的问题

要看到激发这篇文章的陌生感,请将小提琴中的刻度数增加到1000并打破手机的秒表:

CountdownTimer

按下跑步后立即启动秒表,并在看到控制台输出结束后立即停止。显然这是一个粗略的估计,可能会被一秒钟关闭,但它足以看到正在发生的事情。日志的最终输出如下所示:

timer = CountdownTimer(5000, 1000, function(tickNum) { console.log(tickNum) });
timer.start();

我的秒表读取8.5秒。我做了多次测试,结果相似。

问题1 javascript代码如何声称它只需要5004毫秒才能运行,而实际需要花费几秒钟的时间?

问题2 要查看问题的夸张版本并观察CPU的拍摄,请将滴答声调高至2000并再次运行。在这种情况下,我的秒表运行大约需要25秒,最终的数字输出是8727.所以现在代码能够检测到它的运行速度更慢,但它仍然低估了这么慢的速度。这是怎么解释的呢?

JSFiddle代码

999 (index):54
1000 (index):54
5004 

1 个答案:

答案 0 :(得分:2)

  

如果javascript代码声称它只需要5004毫秒才能运行,那么它实际上需要花费几秒钟的时间才能运行吗?

我无法重现。也许你的秒表(或触发它的过程?)是不准确的; new Date通常效果很好。

也许您也只是遇到控制台延迟,它不会经常更新以顺利运行页面。在控制台中显示大量消息可能是瓶颈(不应将它们写入控制台缓冲区)。

  

要查看问题的夸张版本并观察CPU的拍摄,请将嘀嗒声调高至2000并再次运行。在这种情况下,我的秒表运行大约需要25秒,最终的数字输出是8727.所以现在代码能够检测到它的运行速度更慢,但它仍然低估了这么慢的速度。这是怎么解释的呢?

浏览器中实现了最小超时。在5s内有2000个滴答,预计每2.5ms会有一个滴答,这太低了。浏览器无法快速安排滴答。

此外,onFinish()调用总是被推迟一次,即通常在实际结束后大约4ms。

此外,您的optimalPauseTime并非旨在确切,而是为了平等分配超时。当它迟到并试图赶上时,它并没有立即赶上 - 而是将这个追赶分布在剩余的蜱上。相反,你可能会做类似

的事情
function tick() {
  ticksCompleted++;
  tickCb(ticksCompleted);
  if (ticksCompleted == totalTicks) {
    onFinish();
  } else {
    var pause = optimalPauseTime();
    if (pause <= 0)
      tick();
    else
      setTimeout(tick, pause);
  }
}

function optimalPauseTime() {
  var timeElapsed = new Date() - startTime;
  var nextTickRatio = (ticksCompleted + 1) / totalTicks;
  var expectedNextTime = totalMs * nextTickRatio;
  return expectedNextTime - timeElapsed;
}

updated demo