JavaScript setTimeout如此不准确的原因是什么?

时间:2014-01-13 17:19:43

标签: javascript settimeout

我在这里得到了这段代码:

var date = new Date();
setTimeout(function(e) {
    var currentDate = new Date();
    if(currentDate - date >= 1000) {
         console.log(currentDate, date);
         console.log(currentDate-date);
    }
    else {
       console.log("It was less than a second!");
       console.log(currentDate-date);
    }
}, 1000);

在我的计算机中,它总是正确执行,在控制台输出中为1000。有兴趣在其他计算机上,相同的代码,超时回调开始不到一秒钟,currentDate - date的差异在980和998之间。

我知道存在解决这种不准确性的库(例如,Tock)。

基本上,我的问题是:是什么原因,因为setTimeout在指定的延迟时间内没有触发?可能是计算机太慢而浏览器会自动尝试调整缓慢而且之前发生了事件?

PS:以下是Chrome JavaScript控制台中执行的代码和结果的屏幕截图:

Enter image description here

4 个答案:

答案 0 :(得分:22)

它不应该特别准确。有许多因素限制浏览器执行代码的时间;引自MDN

  

除了“钳制”之外,当页面(或操作系统/浏览器本身)忙于执行其他任务时,超时也可能会触发。

换句话说,setTimeout通常实现的方式,只是意味着在浏览器的线程可以自由执行后,在给定的延迟之后执行。

但是,不同的浏览器可能以不同的方式实现它。以下是我做过的一些测试:

var date = new Date();
setTimeout(function(e) {
    var currentDate = new Date();
    console.log(currentDate-date);
}, 1000);

// Browser Test1 Test2 Test3 Test4
// Chrome    998  1014   998   998
// Firefox  1000  1001  1047  1000
// IE 11    1006  1013  1007  1005

也许<来自Chrome的1000次可归因于Date类型的不准确,或者可能是Chrome使用不同的策略来决定何时执行代码 - 也许它正试图将其放入最近的时间段,即使超时延迟尚未完成。

简而言之,如果您期望可靠,一致,毫秒级的时间安排,则不应使用setTimeout

答案 1 :(得分:19)

通常,当尝试执行精度高于50 ms的事物时,计算机程序非常不可靠。这样做的原因是即使在octacore超线程处理器上,操作系统通常也会处理数百个进程和线程,有时甚至是数千个或更多。操作系统通过调度所有这些来完成所有多任务工作,以便一个接一个地获得一段CPU时间,这意味着他们最多只需要几毫秒的时间来完成他们的工作。

这意味着如果你将超时设置为1000毫秒,那么当前浏览器进程甚至不会在那个时间点运行的可能性很小,所以浏览器在1005之前不会注意到这一点是完全正常的。它应该执行给定的回调,1010甚至1050毫秒。

通常这不是问题,它发生了,并且它很少是最重要的。如果是,所有操作系统都提供远远超过1毫秒 精确度的内核级别计时器,并允许开发人员在正确正确的时间点执行代码。但是,作为一个沙盒很大的环境,JavaScript无法访问这样的内核对象,并且浏览器不会使用它们,因为它理论上可以让某人从网页内部攻击操作系统稳定性,通过仔细构建使其他人挨饿的代码线程淹没了许多危险的计时器。

至于为什么测试产生980我不确定 - 这取决于你正在使用哪个浏览器以及哪个JavaScript引擎。然而,我可以完全理解浏览器是否只是手动向下校正系统负载和/或速度,确保“平均延迟仍然是正确的时间” - 从沙盒原理到仅仅是很有意义接近所需的时间而不会给系统的其他部分带来负担。

答案 2 :(得分:7)

如果我误解了这些信息,请有人纠正我:

根据post from John Resig关于跨平台的性能测试的不准确性(强调我的)

  

随着系统时间不断向下舍入到上次查询时间(每个约15毫秒),性能结果的质量会受到严重影响。

因此,与系统时间相比,最多两端15毫米的软糖。

答案 3 :(得分:0)

我也有类似的经历。
我在用这样的东西:

var iMillSecondsTillNextWholeSecond = (1000 - (new Date().getTime() % 1000));
setTimeout(function ()
{
    CountDownClock(ElementID, RelativeTime);
}, iMillSecondsTillNextWholeSecond);//Wait until the next whole second to start.

我注意到它会每隔几秒钟跳过一次,有时会持续更长的时间。
但是,我仍然会在10或20秒后仍在跳过它,它看起来有些摇摇欲坠。
我以为“ 超时时间是否太慢或正在等待其他时间?”。
然后我意识到:“ 也许速度太快了,浏览器管理的计时器关闭了几毫秒?

将+1 MilliSeconds添加到我的变量后,我只看到它跳过了一次。
为了安全起见,我最终增加了+ 50ms。

var iMillSecondsTillNextWholeSecond = (1000 - (new Date().getTime() % 1000) + 50);

我知道,这有点棘手,但是我的计时器现在运行平稳。 :)