浏览器如何确定setInterval应该使用的时间? [英] How do browsers determine what time setInterval should use?

查看:91
本文介绍了浏览器如何确定setInterval应该使用的时间?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通常情况下,浏览器在某些情况下会修改 setInterval 使用的实际时间间隔,甚至超过最小限度。例如,我有以下代码:

It would seem that in general, browsers will in certain cases modify, even beyond a minimum clamp, the actual time interval that setInterval uses. For instance, I have the following code:

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

Here, lastFrames will give us the number of frames over what is approximately the past second. When used in Chrome, Firefox, and Safari, this code doesn't run at one millisecond. Of course, each browser has an arbitrary minimum time between setInterval calls, so this is to be expected. However, as the page continues to run, the frame rate will continue to decrease, even if the tab is still focused. The only way I've found to fix this is to make the browser do something. Something along these lines seems to make the browser run setInterval as fast as it can:

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 更接近我的要求?

Thus, my question is this: What is the browser looking for to justify running setInterval closer to what I ask it to?

编辑:HTML5规范说浏览器不应允许setInterval以低于4ms的间隔运行。

The HTML5 spec says that browsers should not allow setInterval to run at an interval lower than 4ms.

推荐答案

我认为首先,我们必须问自己对Interval函数的期望:

I think first, we have to ask ourselves what we expect from Interval functions:


  • 他们必须保持上下文:你无法可靠地递增计数器的间隔将是非常灾难性的

  • They have to maintain the context: an interval in which you could not increment a counter reliably would be quite disastrous

它们应该执行函数中的任何内容,该函数优先于执行interval(同样在这里,那个计时器必须在我们再次递增之前上升)

They should execute whatever is in the function, which has priority over the execution interval (same here, that timer has to go up before we increment again)

它应该与我们的其余js代码有一个单独的执行堆栈(我们不想等到那些计时器完成他们的业务,直到res t开始执行);

It should have a separate execution stack from the rest of our js code (we don't want to wait for those timers to finish their business until the rest starts executing);

他们应该知道他们所处的环境,无论它有多大(我们希望能够使用jQuery)例如,在我们的计时器函数中。

They should be aware of the context they're in, no matter how vast it is (we want to be able to use jQuery inside our timer functions, for example).

因此,作为 Matt Greer 上面说过,Intervals的设计并不精确,这主要是因为我们并不真正期望它们是精确的,而是在给定的情况下可靠地执行代码时间。

So, as Matt Greer said above, Intervals are by design not exact, and that's mainly because we do not really expect them to be exact, but to be reliably executing code at a given time.

如果您查看Chromium实现,您会看到setTimeout和setInterval的实现基于 DOMTimer :: install ,它会传递执行上下文,动作以及计时器是否为单次

If you have a look at the Chromium implementation, you will see that the Implementation of setTimeout and setInterval is based on the DOMTimer::install, which gets passed the execution context, the action and if the timer is a single shot

这将传递给RunloopTimer,它在系统的帮助下执行循环m计时器(正如你在这里看到的那样)

This gets passed to the RunloopTimer, which executes the loop with the help of system timers (as you see here)

(顺便说一句,在这段时间内,铬安装至少10毫秒,你可以看到这里

(by the way, chromium installs a minimum of 10ms for the interval, as you can see here)

每次执行此操作都是此处处理,在上次执行超过或低于某个时间限制的时间内没有任何断言。

Every Execution of the action is handled here, which does no assertion whatsoever over the time passed since the last execution being over or under a certain timing limit.

相反,它只断言Timer嵌套级别不会太深入,通过减慢使用太多资源/在给定间隔内运行速度太慢的计时器:

In contrary, it only asserts that the Timer nesting level does not get too deep by slowing down timers that are using too much resources / running too slowly for the given interval with this:

if (m_nestingLevel >= maxTimerNestingLevel)
            augmentRepeatInterval(minimumInterval - repeatInterval());
    }

augmentRepeatInterval只是为间隔添加更多毫秒:

augmentRepeatInterval simply adds more milliseconds to the interval:

void augmentRepeatInterval(double delta) { augmentFireInterval(delta); m_repeatInterval += delta; }

那么,我们能得出什么结论呢?

So, what can we conclude?


  • 测量间隔或超时的时间准确度浪费时间。你应该和可以关心的事情是你没有为你想在函数中执行的东西设置太低的间隔。浏览器将尽最大努力及时执行间隔和超时,但不能保证准确的时间。

  • Measuring the time accuracy of Intervals or Timeouts is a waste of time. The thing you should and can care about is that you don't set the intervals too low for the things you want to execute in the function. The browser will do the best it can to execute your intervals and timeouts in a timely fashion, but it won't guarantee exact timing.

间隔执行取决于关于环境,浏览器,实现,版本,上下文,动作本身等等。它并不准确,如果你想用setTimeout或setInterval编写精确的东西,你现在要么疯了,要么以后会发疯。

The Interval execution depends on the environment, the browser, the implementation, the version, the context, the action itself and so on and so on. It is not meant to be exact, and if you want to program something exact with setTimeout or setInterval, you're either crazy now or will go crazy later.

您说在您的代码中,当您对函数添加大量执行时,计时器变得更加准确。这可能是出于不同的原因(可能会获得更多内存,更多独占CPU时间,更多工作人员等等)。我对此很感兴趣,但是你没有提供执行繁重执行的代码。因此,如果你想要一些答案,请提供代码,因为没有它就很难假设。

You said that in your code, when you added heavy execution to the functions, the timer got more accurate. This can be for different reasons (maybe it gets more memory, more exclusive cpu time, more workers and so on). I'm very interested in that, but you did not provide the code that did the heavy execution yet. So if you want some answers on that, please provide the code, because it's difficult to assume anything without it.

但不管是什么让你的间隔运行得更及时,它在任何方面都不可靠。如果你开始在不同的系统上进行测量,你很可能得到各种各样的结果。

But whatever it is that makes your intervals run more timely, it's not reliable in any way. You will most likely get all kinds of various results if you start measuring on different systems.


除此之外,不会导致任何事情的代码也可以进行优化(不执行,浏览器js引擎只执行一次,以更好的方式执行。让我举一个基于你的例子(所有用铬执行的事情):

Other than that, code that does not lead to anything may be as well optimized (not executed, executed only once, executed in a better way) by the browser js engine. Let me give an example based on yours (all things executed in 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' }
}

你会发现在你点击 start 之后的第一次执行需要很长时间,而进一步执行不要(至少在webkit中)。这是因为迭代中的代码不会改变任何东西,浏览器会在第一次执行后识别它并且不再执行它。

You will find that the first execution after you hit start will take ages, whereas the further executions don't (at least in webkit). That's because the code in the iteration does not change anything, and the browser recognizes that after the first execution and simply does not execute it anymore.

让我们看看它是如何改变的执行必须保持绑定到外部变量(在这种情况下 k ):

Let's look how it changes if the execution has to maintain a binding to an outer variable (kin this case):

var k;

function update() {
  console.log(new Date() - lastTime);
  lastTime = new Date();
  for (var i=0; i < 1000000; i++) { k = 'string' + 'string' + 'string' }
}

好的,这里我们对执行时间有一些影响,但它仍然很快。浏览器知道for循环将始终执行相同的操作,但它会执行一次。那么,如果迭代确实创建了一个巨大的字符串,例如呢?

Ok, here we have some impact on the execution time, but it is still quite fast. The browser knows that the for loop will always do the same thing, but it executes it once. So what if the iteration really does create a huge string, for example?

var k;

function update() {
  console.log(new Date() - lastTime);
  lastTime = new Date();
  k = '';
  for (var i=0; i < 1000000; i++) { k += i.toString() }
}

这使得浏览器处于一个受伤的世界,因为它必须返回这个数百万字符的字符串。我们能让这更痛苦吗?

This puts the browser in a world of hurt, because it has to return this millions-of-characters string. Can we make that more painful?

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('') }
}

这个数组连接无法优化,几乎可以慢慢地阻塞任何浏览器。

This array concatenation cannot be optimized and will choke almost any browser slowly and painfully.

因此,在您的情况下,繁重的执行可能导致更多内存被保留并立即被优化器释放。可能是新鲜空气的呼吸和额外的记忆以及闲置的cpu让你的功能快乐地跳起来,但正如我所说,没有看到你繁重的执行代码就没有可靠的东西。

So, in your case, the heavy execution may have lead for more memory to be reserved and instantly freed by the optimizer. May be that breath of fresh air and extra memory and idle cpu made your function jump with joy, but as I said, there's nothing reliable there without having looked at your heavy execution code.

这篇关于浏览器如何确定setInterval应该使用的时间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆