【问题标题】:How do browsers determine what time setInterval should use?浏览器如何确定 setInterval 应该使用什么时间?
【发布时间】:2012-07-13 05:03:54
【问题描述】:

一般来说,浏览器似乎会在某些情况下修改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;
    }
}

在这里,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 毫秒的间隔运行。

【问题讨论】:

  • 每个 HTML5 规范的最小间隔/超时延迟值为 4 毫秒。低于此值的任何内容都将自动重新计算为 4 毫秒。浏览器实现可能会有所不同,因此您不应依赖小于 4 毫秒的时间间隔。这样做也不谨慎。
  • 此外,没有焦点的窗口/选项卡可能会被阻塞到更大的最小间隔时间(例如,1 秒)。
  • @Pointy +1 绝对正确,Firefox 和 Chrome 会将非活动选项卡/窗口的超时/间隔限制为最大 1/秒。
  • 我不确定它到底是什么,感谢您提供的信息。但正如我上面所说,这不是问题所在。
  • 抱歉,我找不到任何其他方式来回答您的问题。如果您的浏览器以某种方式执行间隔快于每 4 毫秒,那么它要么不符合 HTML5,要么具有与其他主要浏览器不兼容的独立实现。现在,如果您想要一种可靠的方法来跟踪用户在您的页面上停留了多长时间,那么问题就略有不同了。

标签: javascript setinterval


【解决方案1】:

setIntervalsetTimeout 根本不准确,也不是设计的。 JavaScript 是单线程的,所以setTimeout/setInterval 基本上是说“把这段代码放在运行队列中。当你到达它时,如果已经过了足够的时间,那么就执行它,否则把它放回队列中并尝试稍后再试”。

如果您将 setInterval 设置为 4 毫秒,但您的应用程序中的事情需要 10 毫秒才能运行,那么 setInterval 就不可能以 4 毫秒运行,最好它会拉出 10 毫秒的间隔。

如果这是出于动画/游戏目的,请尝试requestAnimationFrame。我不知道它是如何实现的,但它确实承诺的一件事是更准确的时间安排。

【讨论】:

  • 我知道它们不准确。这不是这个问题要问的。当没有进行任何处理时,帧速率会稳步下降,但是当有实际工作正在完成时,它会运行得更快。这没有回答我的问题。
  • 我不确定除了开发浏览器和/或操作系统的人之外的任何人如何准确回答您的问题。 setInterval 不保证实际间隔是多少,公开的就是众所周知的。不过,我会密切关注这个问题,因为如果有人可以毫无猜测地回答这个问题,他们确实会得到有趣的信息。
  • @MattGreer - +1。您对 setInterval 的解释是正确的。至于OP,这是他的误解,他并没有简单地将其视为答案。操作系统绝对是降低帧率的问题,这与浏览器本身无关。这些结果会因浏览器周围环境的硬件和软件而有很大差异。
【解决方案2】:

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

  • 他们必须维护上下文:如果你不能可靠地增加一个计数器的时间间隔将是非常灾难性的

  • 他们应该执行函数中的任何内容,其优先级高于执行间隔(此处相同,该计时器必须在我们再次递增之前上升)

  • 它应该有一个独立于我们其余 js 代码的执行堆栈(我们不想等待那些计时器完成其业务,直到其余部分开始执行);

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

所以,正如上面Matt Greer 所说,间隔在设计上并不精确,这主要是因为我们并不真正期望它们是精确的,而是在给定时间可靠地执行代码。

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

p>

这被传递给 RunloopTimer,它在系统计时器(as you see here)的帮助下执行循环

(顺便说一下,chromium 安装间隔最少为 10ms,如你所见here

动作的每次执行都是handled here,它不会在自上次执行超过或低于某个时间限制以来经过的时间进行任何断言。

相反,它只断言定时器嵌套级别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 时间、更多的工人等等)。我对此非常感兴趣,但是您还没有提供执行繁重执行的代码。因此,如果您想对此获得一些答案,请提供代码,因为没有它很难假设任何事情。

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


更新

除此之外,不会导致任何事情的代码可能会被浏览器 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 让您的函数高兴地跳跃,但正如我所说,如果不查看您繁重的执行代码,那里没有什么可靠的。

【讨论】:

  • 我认为这是我将得到的最完整的答案。但是,我希望有更多关于这个主题的文档,特别是关于单个浏览器的优化类型。
【解决方案3】:

我可以想到浏览器可能会在一段时间后减慢您的时间间隔的多种原因(这都是推测,但我认为这就是您所要求的):

  1. 需要安排其他事情来运行,例如垃圾收集等...这需要时间。
  2. 浏览器认为您的间隔计时器占用了过多的 CPU 或消耗了过多的电池,因此它会在一段时间后限制您。一些浏览器记录在标签失去焦点时会执行此操作,因此我可以想象他们也可能在其他时间执行此操作。
  3. 浏览器最初会推迟其他需要执行的工作以支持您的时间间隔,但一段时间后,它不再推迟该工作并且需要一些时间。

【讨论】:

  • 这些看起来很合理,猜想也很棒,但是一些文档肯定会很棒。
  • Chrome 确实记录了它围绕计时器的一些行为。我没有方便的链接,但我知道有一些博客文章介绍了当页面不再是当前选项卡时它是如何工作的。也可能是其他讨论。
【解决方案4】:

问题:What is the browser looking for to justify running setInterval closer to what I ask it to?

回答:没有。它会尽可能快地完成。如果它走得太快,它会等待。 “尽可能”取决于环境。

问题:However, as the page continues to run, the frame rate will continue to decrease, even if the tab is still focused.

这是一个操作系统问题。

当您的代码首次在页面加载时运行时,它会报告 250 作为帧速率,并一直持续到操作系统决定限制浏览器进程的 CPU 或内存分配。浏览器仍然要求以可能的最小时间间隔(在 Firefox 的情况下为 4 毫秒)处理此问题,但操作系统可能真的不在乎。运行 30 分钟后,仍然可以引起操作系统的注意并恢复到 250 帧速率。

只是重申一下,这与浏览器无关。

尝试将其作为“密集循环”,然后尝试解释结果是如何归因于浏览器的:

for (var intense = 0; intense < 10000; intense++) {
        var dec = intense * (5039 + intense);
        for (var processing = 0; processing < intense; processing++) {
            var float = dec / (processing + 1);
        }
}

你应该在运行它时听到你的 cpu 哭泣,除非你有一台非常棒的电脑(它与浏览器绝对没有有关)。

【讨论】:

    【解决方案5】:

    首先,如果您正在制作图形(帧的说法表明您是),您可能应该使用requestAnimationFrame 而不是 setInterval。

    我在 chrome 20 中运行了您的代码,但在最初的几分钟内我没有看到低于 244 的帧速率。你看到与此不同的东西吗?大多数帧速率都在 249 或 250,这是使用 setInterval 时规范允许的最大值,即使在运行一段时间后也是如此。

    在很长一段时间内,我的平均每秒帧数为 247 帧,这种浪费很容易归因于不合时宜的垃圾收集。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-18
      • 1970-01-01
      • 1970-01-01
      • 2011-02-09
      • 2012-08-24
      • 2010-10-27
      相关资源
      最近更新 更多