我认为首先,我们必须问自己对 Interval 函数的期望:
他们必须维护上下文:您无法可靠地增加计数器的时间间隔将是非常灾难性的
他们应该执行函数中的任何内容,其优先级高于执行间隔(此处相同,该计时器必须在我们再次递增之前上升)
它应该与我们的其余 js 代码有一个单独的执行堆栈(我们不想等待那些计时器完成他们的业务,直到其余的开始执行);
他们应该知道他们所处的上下文,无论它有多大(例如,我们希望能够在我们的计时器函数中使用 jQuery)。
所以,正如马特·格里尔(Matt Greer)上面所说,间隔在设计上并不精确,这主要是因为我们并不真正期望它们是精确的,而是在给定时间可靠地执行代码。
如果您查看 Chromium 实现,您会看到 setTimeout 和 setInterval 的实现基于DOMTimer::install,它会传递执行上下文、动作以及计时器是否为单次
这将传递给 RunloopTimer,后者在系统计时器的帮助下执行循环(如您在此处看到的)
(顺便说一下,Chromium 至少安装了 10ms 的间隔,如您在此处看到的)
动作的每一次执行都在这里处理,自上次执行超过或低于某个时间限制以来,它在过去的时间里不做任何断言。
相反,它仅通过减慢使用过多资源/在给定时间间隔内运行速度过慢的计时器来断言计时器嵌套级别不会变得太深,如下所示:
if (m_nestingLevel >= maxTimerNestingLevel)
augmentRepeatInterval(minimumInterval - repeatInterval());
}
augmentRepeatInterval 只是将更多毫秒添加到间隔:
void augmentRepeatInterval(double delta) { augmentFireInterval(delta); m_repeatInterval += delta; }
那么,我们能得出什么结论呢?
测量 Intervals 或 Timeouts 的时间准确性是浪费时间。您应该并且可以关心的是,对于要在函数中执行的事情,不要将间隔设置得太低。浏览器将尽其所能及时执行您的时间间隔和超时,但它不能保证准确的时间。
Interval 执行取决于环境、浏览器、实现、版本、上下文、操作本身等等。这并不意味着精确,如果你想用 setTimeout 或 setInterval 编写一些精确的东西,你要么现在疯了,要么以后会疯。
您说在您的代码中,当您向函数添加大量执行时,计时器变得更加准确。这可能是出于不同的原因(也许它获得了更多的内存、更多的独占 CPU 时间、更多的工人等等)。我对此非常感兴趣,但是您还没有提供执行繁重执行的代码。因此,如果您想要一些答案,请提供代码,因为没有它很难假设任何事情。
但无论是什么让你的间歇跑得更及时,它在任何方面都不可靠。如果您开始在不同的系统上进行测量,您很可能会得到各种不同的结果。
更新
除此之外,不会导致任何事情的代码可能会被浏览器 js 引擎优化(不执行,只执行一次,以更好的方式执行)。让我举一个基于你的例子(所有东西都用铬执行):
function start() {
window.setInterval(function() {
update();
}, 1);
}
lastTime = new Date;
numFrames = 0;
lastFrames = 0;
function update() {
console.log(new Date() - lastTime);
lastTime = new Date();
for (var i=0; i < 1000000; i++) { var k = 'string' + 'string' + 'string' }
}
您会发现点击后的第一次执行start
需要很长时间,而进一步的执行则不需要(至少在 webkit 中)。那是因为迭代中的代码没有改变任何东西,并且浏览器在第一次执行后识别出它并且不再执行它。
让我们看看如果执行必须保持与外部变量的绑定(k
在本例中),它会如何变化:
var k;
function update() {
console.log(new Date() - lastTime);
lastTime = new Date();
for (var i=0; i < 1000000; i++) { k = 'string' + 'string' + 'string' }
}
好的,这里我们对执行时间有一些影响,但还是相当快的。浏览器知道 for 循环总是做同样的事情,但它只执行一次。那么,例如,如果迭代确实创建了一个巨大的字符串呢?
var k;
function update() {
console.log(new Date() - lastTime);
lastTime = new Date();
k = '';
for (var i=0; i < 1000000; i++) { k += i.toString() }
}
这使浏览器陷入了痛苦的境地,因为它必须返回这个数百万个字符的字符串。我们能让这更痛苦吗?
var k;
function update() {
console.log(new Date() - lastTime);
lastTime = new Date();
k = '';
for (var i=0; i < 1000000; i++) { k = ['hey', 'hey', 'hey'].join('') }
}
这种数组连接无法优化,并且会缓慢而痛苦地阻塞几乎所有浏览器。
因此,在您的情况下,繁重的执行可能会导致更多内存被保留并由优化器立即释放。可能是新鲜空气、额外内存和空闲 cpu 让你的函数高兴地跳跃,但正如我所说,如果没有查看你繁重的执行代码,那里没有什么可靠的。