Blo*_*ust 6 javascript 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;
}
}
Run Code Online (Sandbox Code Playgroud)
在这里,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.
}
Run Code Online (Sandbox Code Playgroud)
因此,我的问题是:浏览器在寻找什么来证明setInterval更接近我的要求?
编辑:HTML5规范说,浏览器不应允许setInterval以低于4毫秒的间隔运行.
setInterval并且setTimeout根本不准确,并且不是设计成的.JavaScript是单线程的,所以setTimeout/setInterval基本上说"把这块代码放在运行队列中.当你到达它时,如果已经过了足够的时间,那么执行它,否则将它重新放回队列中,然后再试一次" .
如果你将setInterval设置为4毫秒,但是应用程序中的东西需要10毫秒才能运行,那么setInterval就无法在4毫秒内运行,最好它会以10毫秒的间隔运行.
如果这是为了动画/游戏目的,那么试试requestAnimationFrame.我不知道它是如何实现的,但它承诺的一件事是更精确的计时.
我认为首先,我们必须问自己我们对区间函数的期望:
他们必须维护上下文:无法可靠地增加计数器的时间间隔将是相当灾难性的
他们应该执行函数中的任何内容,该函数的优先级高于执行间隔(同样,在我们再次递增之前,计时器必须上升)
它应该有一个与我们的其余 js 代码分开的执行堆栈(我们不想等待那些计时器完成其业务,直到其余的开始执行);
他们应该知道自己所处的上下文,无论它有多大(例如,我们希望能够在计时器函数中使用 jQuery)。
因此,正如Matt Greer上面所说,间隔在设计上并不精确,这主要是因为我们并不真正期望它们是精确的,而是在给定时间可靠地执行代码。
如果您查看 Chromium 实现,您会发现 setTimeout 和 setInterval 的实现基于DOMTimer::install,它传递执行上下文、操作以及计时器是否为单次触发
它被传递给 RunloopTimer,它在系统计时器的帮助下执行循环(如您在此处看到的)
(顺便说一句,Chromium 安装的间隔至少为 10 毫秒,如您在此处看到的)
操作的每次执行都在此处处理,自上次执行超过或低于特定时间限制以来,它不会进行任何断言。
相反,它只是通过减慢在给定时间间隔内使用太多资源/运行太慢的计时器来断言计时器嵌套级别不会太深:
if (m_nestingLevel >= maxTimerNestingLevel)
augmentRepeatInterval(minimumInterval - repeatInterval());
}
Run Code Online (Sandbox Code Playgroud)
AugmentRepeatInterval 只是在间隔中添加更多毫秒:
void augmentRepeatInterval(double delta) { augmentFireInterval(delta); m_repeatInterval += delta; }
Run Code Online (Sandbox Code Playgroud)
那么,我们可以得出什么结论呢?
测量间隔或超时的时间准确性是浪费时间。您应该并且可以关心的是,不要将函数中要执行的操作的间隔设置得太低。浏览器将尽最大努力及时执行您的间隔和超时,但它不能保证准确的时间。
间隔执行取决于环境、浏览器、实现、版本、上下文、操作本身等等。它并不意味着精确,如果你想用 setTimeout 或 setInterval 编写精确的东西,你要么现在疯了,要么以后会疯。
您说在您的代码中,当您向函数添加大量执行时,计时器变得更加准确。这可能有不同的原因(也许它获得更多内存、更多独占 CPU 时间、更多工作线程等等)。我对此非常感兴趣,但您尚未提供执行繁重执行的代码。因此,如果您想得到一些答案,请提供代码,因为没有代码就很难做出任何假设。
但无论是什么让你的间隔跑得更及时,它都不可靠。如果您开始在不同的系统上进行测量,您很可能会得到各种不同的结果。
除此之外,浏览器 js 引擎可能会优化那些不会产生任何结果的代码(不执行、只执行一次、以更好的方式执行)。让我根据你的例子给出一个例子(所有的事情都是在 chromium 中执行的):
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' }
}
Run Code Online (Sandbox Code Playgroud)
你会发现点击后的第一次执行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' }
}
Run Code Online (Sandbox Code Playgroud)
好的,这里我们对执行时间有一些影响,但仍然相当快。浏览器知道 for 循环总是做同样的事情,但它只执行一次。例如,如果迭代确实创建了一个巨大的字符串怎么办?
var k;
function update() {
console.log(new Date() - lastTime);
lastTime = new Date();
k = '';
for (var i=0; i < 1000000; i++) { k += i.toString() }
}
Run Code Online (Sandbox Code Playgroud)
这使浏览器陷入了痛苦的境地,因为它必须返回这个数百万个字符的字符串。我们能让这更痛苦吗?
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('') }
}
Run Code Online (Sandbox Code Playgroud)
这种数组串联无法优化,并且会缓慢而痛苦地阻塞几乎所有浏览器。
因此,在您的情况下,繁重的执行可能会导致优化器保留并立即释放更多内存。也许呼吸新鲜空气、额外的内存和空闲的 CPU 让你的函数高兴地跳跃,但正如我所说,如果不查看你的繁重的执行代码,就没有什么可靠的东西。
| 归档时间: |
|
| 查看次数: |
1242 次 |
| 最近记录: |