标签或窗口处于非活动状态时,浏览器如何暂停/更改Javascript?


168

背景:我正在做一些用户界面测试,这些测试需要检测人们是否在关注。但是,这个问题是不是有关的页面知名度API

具体来说,我想知道如果在不同的浏览器中当前选项卡处于不活动状态或浏览器窗口处于不活动状态,我的Javascript代码将受到什么影响。到目前为止,我已经挖掘了以下内容:

我有以下问题:

  • 除了移动浏览器之外,桌面浏览器是否会在选项卡处于非活动状态时暂停JS执行?什么时候使用哪些浏览器?
  • 哪些浏览器减少setInterval重复次数?它只是减少到一个极限还是一个百分比?例如,如果我有一个10ms的重复与一个5000ms的重复,那么每个都会受到影响吗?
  • 如果窗口不在焦点上,而不是仅在选项卡上,这些变化会发生吗?(我想这将更难检测,因为它需要OS API。)
  • 在活动选项卡中是否还有其他效果无法观察到?他们会搞乱本来可以正确执行的事情(即上述的茉莉花测试)吗?

如果暂停,则Facebook之类的网站将不会在后台标签上收到任何聊天消息。
约瑟夫

1
是啊有没有间歇,但我记得读了setInterval/ setTimeout下1000ms的时间变更为1000毫秒,当标签/窗口模糊
伊恩

19
@ProfPickle网站管理员?真?这是一个JS编程问题。
Andrew Mao

1
选项卡/窗口模糊时,@ lan setInterval/ setTimeout1000ms以下的时间更改为1000ms。不太清楚你想传达什么
阿莫尔中号库尔卡尼

4
+1好问题。最好并排比较浏览器的性能,因为我认为选项卡不活动时的夹紧行为不是任何标准的一部分。
UpTheCreek

Answers:


190

测试一

我为此专门编写了一个测试:
帧速率分布:setInterval与requestAnimationFrame

注意:此测试占用大量CPU。requestAnimationFrameIE 9-和Opera 12-不支持。

该测试记录setIntervalrequestAnimationFrame在不同的浏览器中运行所需的实际时间,并以分发的形式为您提供结果。您可以更改毫秒数,setInterval以查看其在不同设置下的运行方式。延迟方面的setTimeout工作与a类似setIntervalrequestAnimationFrame通常默认设置为60fps,具体取决于浏览器。要查看切换到其他选项卡或不活动窗口时发生的情况,只需打开页面,切换到其他选项卡,然后等待一段时间。它将继续在不活动的选项卡中记录这些功能的实际时间。

测试二

测试它的另一种方法是使用setInterval和重复记录时间戳,并requestAnimationFrame在独立的控制台中查看它。使选项卡或窗口处于非活动状态时,您可以查看其更新频率(或者是否曾经更新)。

结果

当该标签页处于非活动状态时,Chrome浏览
器将最小间隔限制为setInterval1000毫秒左右。如果间隔大于1000ms,它将以指定的间隔运行。窗口是否聚焦不重要,仅当您切换到其他选项卡时,间隔才会受到限制。requestAnimationFrame选项卡处于非活动状态时被暂停。

// Provides control over the minimum timer interval for background tabs.
const double kBackgroundTabTimerInterval = 1.0;

https://codereview.chromium.org/6546021/patch/1001/2001

Firefox
与Chrome相似,setInterval当选项卡(而非窗口)处于非活动状态时,Firefox将最小间隔限制为1000毫秒左右。但是,requestAnimationFrame当选项卡处于非活动状态时,其运行速度将呈指数级下降,每个帧占用1s,2s,4s,8s等。

// The default shortest interval/timeout we permit
#define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms
#define DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms

https://hg.mozilla.org/releases/mozilla-release/file/0bf1cadfb004/dom/base/nsGlobalWindow.cpp#l296

Internet Explorer
IE不会限制setInterval选项卡处于非活动状态时的延迟,但是会在选项卡处于非活动状态时暂停requestAnimationFrame。窗口是否聚焦不重要。

边缘
从边缘14开始,setInterval在不活动的选项卡中,上限为1000ms。requestAnimationFrame总是在非活动标签中暂停。

Safari
就像Chrome,Safari帽一样setInterval该标签页处于非活动状态时为1000毫秒。requestAnimationFrame也暂停了。

Opera
自从采用Webkit引擎以来,Opera表现出与Chrome相同的行为。setInterval上限为1000毫秒,requestAnimationFrame在该标签页处于非活动状态时会暂停。

摘要

非活动标签的重复间隔:

           setInterval      requestAnimationFrame
 Chrome
9-不受影响,不支持
10个不受影响的暂停
11+> = 1000ms暂停

火狐浏览器
3-不受影响不支持
4个未受影响的1s
5+> = 1000毫秒2 Ñ S(N =由于不活动帧的数量)

IE浏览器
9-不受影响,不支持
10+不受影响已暂停

边缘
13-不受影响暂停
14+> = 1000ms暂停

苹果浏览器
5-不受影响,不支持
6不受影响暂停
7+> = 1000ms暂停

歌剧
12-不受影响,不受支持
15+> = 1000ms暂停

好答案。除setInterval和以外的其他功能可能存在其他已知差异requestAnimationFrame
安德鲁·毛

1
@AndrewMao不是我知道的。我在使用库可靠地检测JS是否使用setInterval和重新启用JS时遇到了这个问题requestAnimationFrame。我所知道的是,它的setTimeout行为与相似setInterval,因为它们在Firefox和Chrome中具有相同的最小背景间隔,在其他浏览器中没有明显的限制。
安东尼

2
Firefox的setInterval的最小值能明显打开的URL来改变about:config浏览器和改变dom.min_background_timeout_value价值的东西比其他1000
乔纳斯·柏林

当浏览器最小化时,我可以每5秒使用它重新加载页面吗?是我的问题。
shaijut

1
请注意,requestAnimationFrame如果用户只是切换应用程序(Chrome中的Alt + Tab),Chrome 不会暂停/降低调用速度。只要该标签在Chrome中处于活动状态,“帧速率”就或多或少是恒定的。
Marc

11

我观察到的结果:在Chrome的非活动标签上,所有少于1000毫秒setTimeout(必须相同setInterval)的等待时间都四舍五入为1000毫秒。我认为更长的超时时间不会被修改。

Chrome 11Firefox 5.0以来似乎是这种行为:https : //developer.mozilla.org/en-US/docs/DOM/window.setTimeout#Inactive_tabs

此外,当整个窗口处于非活动状态时,我不认为它会以这种方式运行(但似乎很容易调查)。


1
jQuery focusblurevent似乎都可以检测到选项卡和窗口切换,因此可以想象它可以同时运行。但我想知道窗口如何检测它是否实际可见。
Andrew Mao

2
实际上,由于它是内部浏览器实现,因此它与jQueryJavascript没有任何关系。

您现在可以在2016年末确认吗?
vsync

0

一个新的答案可以补充这些问题:在Chrome 78.0.3904.108上,我注意到所有这些超时(不仅是1000ms以下的超时)花费的时间比我移至其他选项卡然后再返回时的预期时间长。我看到的行为被更正确地描述为“不活动的选项卡上的所有超时都可能会延迟一些额外的时间,最长为1000ms。”

let timeouts = [ 500, 1000, 2000, 3000, 10000 ];

let minExcess = document.getElementsByClassName('minExcess')[0];

timeouts.forEach(ms => {
  let elem = document.getElementsByClassName(`t${ms}`)[0];
  let cnt = 0;
  
  let lastMs = +new Date();
  let f = () => {
    let curMs = +new Date();
    let disp = document.createElement('p');
    let net = curMs - lastMs;
    lastMs = curMs;
        
    setTimeout(f, ms);
    if (minExcess.value && (net - ms) < parseInt(minExcess.value)) return;
    
    disp.innerText = `${net},`;
    elem.appendChild(disp);
    if (++cnt > 10) elem.firstElementChild.remove();
    
  };
  setTimeout(f, ms);
  
});
body { font-size: 80%; }
div {
  max-height: 80px;
  overflow-x: auto;
  background-color: rgba(0, 0, 0, 0.1);
  margin-bottom: 2px;
  white-space: nowrap;
}
p { margin: 0; }
div > p {
  margin: 0;
  display: inline-block;
  vertical-align: top;
  margin-right: 2px;
}
input { margin: 0 0 10px 0; }
.t500:before { display: block; content: '500ms'; font-weight: bold; }
.t1000:before { display: block; content: '1000ms'; font-weight: bold; }
.t2000:before { display: block; content: '2000ms'; font-weight: bold; }
.t3000:before { display: block; content: '3000ms'; font-weight: bold; }
.t10000:before { display: block; content: '10000ms'; font-weight: bold; }
<p>Ignore any values delayed by less than this amount:</p>
<input type="text" class="minExcess" value="200" pattern="^[0-9]*$"/>
<div class="timeout t500"></div>
<div class="timeout t1000"></div>
<div class="timeout t2000"></div>
<div class="timeout t3000"></div>
<div class="timeout t10000"></div>

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.