function start() { window.setInterval(function() { update(); },1); } lastTime = new Date; numFrames = 0; lastFrames = 0; function update() { numFrames++; if (new Date - lastTime >= 1000) { lastFrames = numFrames; numFrames = 0; lastTime = new Date; } }
在这里,lastFrames将为我们提供大约过去第二帧的帧数.在Chrome,Firefox和Safari中使用时,此代码不会在一毫秒内运行.当然,每个浏览器在setInterval调用之间都有一个任意的最小时间,所以这是可以预料的.但是,随着页面继续运行,即使选项卡仍处于焦点,帧速率也将继续降低.我发现修复此问题的唯一方法是让浏览器执行某些操作.沿着这些方向的东西似乎使浏览器尽可能快地运行setInterval:
function start() { window.setInterval(function() { update(); },1); } lastTime = new Date; numFrames = 0; lastFrames = 0; function update() { numFrames++; if (new Date - lastTime >= 1000) { lastFrames = numFrames; numFrames = 0; lastTime = new Date; } //doIntensiveLoop,processing,etc. }
因此,我的问题是:浏览器寻找什么来证明运行setInterval更接近我的要求?
编辑:HTML5规范说,浏览器不应允许setInterval以低于4毫秒的间隔运行.
解决方法
>他们必须保持上下文:你无法可靠地增加一个计数器的间隔将是非常灾难性的
>它们应该执行函数中的任何内容,该函数优先于执行间隔(此处相同,定时器必须在我们再次递增之前上升)
>它应该与我们的其余js代码有一个单独的执行堆栈(我们不想等待那些定时器完成他们的业务,直到其余的开始执行);
>他们应该知道他们所处的环境,无论它有多大(我们希望能够在我们的计时器函数中使用jQuery).
因此,正如Matt Greer所述,Intervals的设计并不精确,这主要是因为我们并不真正期望它们是精确的,而是在给定时间可靠地执行代码.
如果您查看Chromium实现,您将看到setTimeout和setInterval的实现基于DOMTimer::install,它将传递执行上下文,操作以及计时器是否为单击
这将传递给RunloopTimer,它在系统定时器(as you see here)的帮助下执行循环
(顺便说一下,铬的间隔至少安装10毫秒,你可以看到here)
该操作的每次执行都是handled here,在上次执行超过或低于某个时间限制的时间内没有任何断言.
相反,它只通过减慢使用过多资源/在给定时间间隔内运行速度太慢的定时器来断言Timer嵌套级别does not get too deep:
if (m_nestingLevel >= maxTimerNestingLevel) augmentRepeatInterval(minimumInterval - repeatInterval()); }
augmentRepeatInterval只是为间隔添加更多毫秒:
void augmentRepeatInterval(double delta) { augmentFireInterval(delta); m_repeatInterval += delta; }
那么,我们可以得出什么结论呢?
>测量间隔或超时的时间精度是浪费时间.你应该和可以关心的事情是你没有为你想在函数中执行的东西设置太低的间隔.浏览器将尽最大努力及时执行间隔和超时,但不能保证准确的时间安排.
> Interval执行取决于环境,浏览器,实现,版本,上下文,操作本身等等.它并不准确,如果你想用setTimeout或setInterval编写精确的东西,你现在要么疯了,要么以后会发疯.
>您说在您的代码中,当您对函数添加大量执行时,计时器变得更加准确.这可能是出于不同的原因(可能会获得更多内存,更多独占cpu时间,更多工作人员等等).我对此很感兴趣,但是你没有提供执行繁重执行的代码.因此,如果你想要一些答案,请提供代码,因为没有它就很难假设.
>但不管是什么让你的间隔运行更及时,它在任何方面都不可靠.如果您开始在不同系统上进行测量,您很可能会得到各种各样的结果.
UPDATE
除此之外,浏览器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' } }
你会发现在你点击开始后的第一次执行需要很长时间,而进一步的执行则不会(至少在webkit中).这是因为迭代中的代码不会改变任何东西,浏览器会在第一次执行后识别出它并且不再执行它.
让我们看一下如果执行必须保持与外部变量的绑定(在本例中为kin),它会如何变化:
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使你的功能快乐地跳起来,但正如我所说的那样,没有看到你繁重的执行代码就没有什么可靠的.