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

192

背景:我正在进行一些用户界面测试,需要检测人们是否专注。但是,这个问题与页面可见性API无关。

具体而言,我想知道如果当前标签页不活动或浏览器窗口不活动,在不同浏览器中我的Javascript代码将受到何种影响。到目前为止,我已经找到了以下信息:

我有以下问题:

  • 除移动浏览器外,桌面浏览器是否会在标签页不活动时暂停JS执行?何时以及哪些浏览器会这样做?
  • 哪些浏览器会减少setInterval的重复?它是仅限于某个限制还是按百分比减少?例如,如果我有一个10ms的重复与5000ms的重复,各自将受到怎样的影响?
  • 如果窗口失去焦点,而不仅仅是标签页,是否会发生这些更改?(我想这可能更难检测,因为它需要操作系统API。)
  • 是否存在其他不会在活动选项卡中观察到的影响?它们会否破坏本应该正确执行的事情(即前面提到的Jasmine测试)?

如果它们被暂停了,像Facebook这样的网站就不会在后台标签上接收任何聊天消息。 - Joseph
1
是的,没有暂停,但我记得阅读过当选项卡/窗口失焦时,setInterval/setTimeout时间低于1000ms会更改为1000ms。 - Ian
1
当浏览器标签页/窗口失去焦点时,小于1000毫秒的 setInterval/ setTimeout时间被更改为1000毫秒。不清楚您要传达什么信息。 - Amol M Kulkarni
4
好的问题。如果能看到浏览器行为的并排比较就好了,因为我认为在选项卡不活动时进行夹紧行为并不属于任何标准。 - UpTheCreek
当标签处于活动状态时,我只能获得大约每秒200次的延迟执行,而在非活动状态下每秒只有1次。 - dandavis
显示剩余4条评论
3个回答

217

测试一

我为此编写了一个特定的测试:
帧率分布:setInterval vs requestAnimationFrame

注意:此测试对CPU要求较高。IE 9- 和 Opera 12- 不支持requestAnimationFrame

该测试记录了setIntervalrequestAnimationFrame在不同浏览器中运行所需的实际时间,并以分布形式呈现结果。您可以更改setInterval的毫秒数,以查看不同设置下的运行情况。setTimeout与延迟相关类似于setInterval。在大多数浏览器中,默认情况下requestAnimationFrame通常默认为每秒60帧。为了查看在切换到其他选项卡或有不活动窗口时会发生什么,请打开页面,切换到其他选项卡并等待一段时间。它将继续记录这些函数所需的实际时间。

测试二

另一种测试方法是使用setIntervalrequestAnimationFrame重复记录时间戳,并在分离的控制台中查看它。在您使选项卡或窗口不活动时,您可以看到它更新的频率(或是否更新)。

结果

Chrome
在选项卡不活动时,Chrome将setInterval的最小间隔限制为约1000ms。如果间隔大于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类似,当选项卡(而不是窗口)处于非活动状态时,Firefox会将setInterval的最小间隔限制在大约1000ms。然而,requestAnimationFrame在选项卡处于非活动状态时以指数方式变慢,每帧需要1秒、2秒、4秒、8秒等。

// 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。窗口是否失去焦点都没有影响。

Edge
从Edge 14开始,在非活跃标签页中,setInterval被限制为1000ms。requestAnimationFrame总是在非活跃标签页中暂停。

Safari
就像Chrome一样,当标签页不活跃时,Safari将setInterval限制在1000ms。同时requestAnimationFrame也会暂停。

Opera
自采用Webkit引擎以来,Opera表现出与Chrome相同的行为。当标签页不活跃时,setInterval被限制在1000ms,而requestAnimationFrame被暂停。

总结

非活跃标签页中的重复间隔:

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

Firefox
3-         不受影响               不支持
4          不受影响               1s
5+         >=1000ms            2nns (n = inactivity以来的帧数)

IE
9-         不受影响               不支持
10+        不受影响               暂停

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

Safari
5-         不受影响               不支持
6          不受影响               暂停
7+         >=1000ms            暂停

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

很好的回答。除了setIntervalrequestAnimationFrame之外,还有其他可能已知的函数差异吗? - Andrew Mao
1
@AndrewMao 我不知道有没有这样的情况。我在开发一个库时遇到了这个问题,该库可靠地检测JS是否使用setIntervalrequestAnimationFrame重新启用。我知道的是,setTimeout的行为类似于setInterval,因为它们在Firefox和Chrome中具有相同的最小后台间隔,并且在其他浏览器中没有明显的限制。 - Antony
2
Firefox的setInterval最小值可以通过在浏览器中打开URL“about:config”,并将“dom.min_background_timeout_value”值更改为1000以外的其他值来更改。 - Jonas Berlin
我可以使用这个方法在浏览器最小化时每5秒重新加载页面吗?这是我的问题链接:http://stackoverflow.com/questions/38376157/javascript-reload-page-every-5-seconds-when-browser-is-minimized。 - Shaiju T
1
请注意,如果用户仅仅切换应用程序(从 Chrome 中按下 Alt+Tab),Chrome 不会暂停/减少对 requestAnimationFrame 的调用速率。只要在 Chrome 中该选项卡处于活动状态,“帧率”就是相对稳定的。 - Marc
@JonasBerlin 太棒了!这可以立即生效,无需重新启动浏览器。 - igor

12
我观察到:在Chrome的非活动标签中,所有小于1000毫秒的setTimeout(对setInterval也是如此)都会被四舍五入为1000毫秒。我认为较长的超时时间不会被修改。 自Chrome 11和Firefox 5.0以来就是这种行为:https://developer.mozilla.org/en-US/docs/DOM/window.setTimeout#Inactive_tabs 此外,当整个窗口处于非活动状态时,我不认为它会表现出这种方式(但很容易调查)。

1
jQuery的focusblur事件似乎可以检测到选项卡和窗口切换,因此它可能可以双向工作。但我想知道窗口如何检测自己是否实际可见。 - Andrew Mao
2
实际上,它与 jQuery 或 Javascript 没有任何关联,因为它是浏览器内部的实现。 - user1321425
你现在可以确认这个问题吗?现在已经是2016年末了。 - vsync

1

以下是一个更新的答案:在Chrome 78.0.3904.108上,我注意到所有这些超时(不仅仅是1000毫秒以下的)在我切换到另一个标签页后需要比预期更长的时间。我看到的行为更正确地描述为"所有非活动标签页上的超时可能会被延迟一段额外的时间,最多为1000毫秒。"尝试运行以下代码并切换到另一个标签页!

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>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接