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

时间:2013-04-08 05:36:31

标签: javascript browser

背景:我正在做一些需要检测人们是否注意的用户界面测试。但是,关于the page visibility API,这个问题

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

我有以下问题:

  • 除了移动浏览器之外,当选项卡不活动时,桌面浏览器是否会暂停JS执行?什么时候和哪些浏览器?
  • 哪些浏览器会减少setInterval重复?它只是减少到一个限度或一个百分比?例如,如果我有10毫秒重复而不是5000毫秒重复,那么每个都会受到影响吗?
  • 如果窗口失焦,是否会发生这些变化,而不仅仅是标签? (我想它会更难检测,因为它需要OS API。)
  • 在活动标签中是否还有其他效果?他们会把那些本来可以正确执行的事情弄糟(即前面提到的Jasmine测试)吗?

3 个答案:

答案 0 :(得分:172)

测试一

我专门为此目的编写了一个测试:
Frame Rate Distribution: setInterval vs requestAnimationFrame

注意:此测试非常占用CPU。 IE 9-和Opera 12不支持requestAnimationFrame

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

测试二

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

结果

<强>铬
当标签处于非活动状态时,Chrome会将setInterval的最小间隔限制为大约1000毫秒。如果间隔高于1000毫秒,它将以指定的间隔运行。窗口是否失焦并不重要,只有当您切换到不同的选项卡时才会限制间隔。选项卡处于非活动状态时,requestAnimationFrame暂停。

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

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

<强>火狐
与Chrome类似,当标签页(不是窗口)处于非活动状态时,Firefox将setInterval的最小间隔限制为大约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在非活动选项卡中的上限为1000毫秒。 requestAnimationFrame始终在非活动标签页中暂停。

<强> Safari浏览器
就像Chrome一样,当标签处于非活动状态时,Safari会在1000毫秒时限制setIntervalrequestAnimationFrame也暂停了。

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

摘要

重复非活动标签的间隔:

           setInterval     requestAnimationFrame
Chrome
9-         not affected    not supported
10         not affected    paused
11+        >=1000ms        paused

Firefox
3-         not affected    not supported
4          not affected    1s
5+         >=1000ms        2ns (n = number of frames since inactivity)

IE
9-         not affected    not supported
10+        not affected    paused

Edge
13-        not affected    paused
14+        >=1000ms        paused

Safari
5-         not affected    not supported
6          not affected    paused
7+         >=1000ms        paused

Opera
12-        not affected    not supported
15+        >=1000ms        paused

答案 1 :(得分:10)

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

似乎是 Chrome 11 Firefox 5.0 之后的行为:https://developer.mozilla.org/en-US/docs/DOM/window.setTimeout#Inactive_tabs

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

答案 2 :(得分: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>