Javascript 当 Chrome 中的选项卡处于非活动状态时,如何使 setInterval 也能工作?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5927284/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I make setInterval also work when a tab is inactive in Chrome?
提问by pimvdb
I have a setInterval
running a piece of code 30 times a second. This works great, however when I select another tab (so that the tab with my code becomes inactive), the setInterval
is set to an idle state for some reason.
我setInterval
每秒运行一段代码 30 次。这很好用,但是当我选择另一个选项卡时(以便我的代码的选项卡变为非活动状态),setInterval
出于某种原因设置为空闲状态。
I made this simplified test case (http://jsfiddle.net/7f6DX/3/):
我做了这个简化的测试用例(http://jsfiddle.net/7f6DX/3/):
var $div = $('div');
var a = 0;
setInterval(function() {
a++;
$div.css("left", a)
}, 1000 / 30);
If you run this code and then switch to another tab, wait a few seconds and go back, the animation continues at the point it was when you switched to the other tab. So the animation isn't running 30 times a second in case the tab is inactive. This can be confirmed by counting the amount of times the setInterval
function is called each second - this will not be 30 but just 1 or 2 if the tab is inactive.
如果您运行此代码然后切换到另一个选项卡,等待几秒钟然后返回,动画会在您切换到另一个选项卡时继续。因此,如果选项卡处于非活动状态,动画不会每秒运行 30 次。这可以通过计算setInterval
每秒调用函数的次数来确认- 如果选项卡处于非活动状态,这将不是 30,而是 1 或 2。
I guess that this is done by design so as to improve performance, but is there any way to disable this behaviour? It is actually a disadvantage in my scenario.
我猜这是为了提高性能而设计的,但是有什么方法可以禁用这种行为?在我的情况下,这实际上是一个缺点。
采纳答案by 2grit
On most browsers inactive tabs have low priority execution and this can affect JavaScript timers.
在大多数浏览器上,非活动选项卡的执行优先级较低,这会影响 JavaScript 计时器。
If the values of your transition were calculated using real time elapsed between framesinstead fixed increments on each interval, you not only workaround this issue but also can achieve a smother animation by using requestAnimationFrameas it can get up to 60fps if the processor isn't very busy.
如果您的过渡值是使用帧之间经过的实时时间而不是每个间隔的固定增量来计算的,您不仅可以解决这个问题,还可以通过使用requestAnimationFrame来实现更流畅的动画,因为如果处理器不是,它可以达到 60fps很忙。
Here's a vanilla JavaScript example of an animated property transition using requestAnimationFrame
:
这是一个使用 的动画属性转换的普通 JavaScript 示例requestAnimationFrame
:
var target = document.querySelector('div#target')
var startedAt, duration = 3000
var domain = [-100, window.innerWidth]
var range = domain[1] - domain[0]
function start() {
startedAt = Date.now()
updateTarget(0)
requestAnimationFrame(update)
}
function update() {
let elapsedTime = Date.now() - startedAt
// playback is a value between 0 and 1
// being 0 the start of the animation and 1 its end
let playback = elapsedTime / duration
updateTarget(playback)
if (playback > 0 && playback < 1) {
// Queue the next frame
requestAnimationFrame(update)
} else {
// Wait for a while and restart the animation
setTimeout(start, duration/10)
}
}
function updateTarget(playback) {
// Uncomment the line below to reverse the animation
// playback = 1 - playback
// Update the target properties based on the playback position
let position = domain[0] + (playback * range)
target.style.left = position + 'px'
target.style.top = position + 'px'
target.style.transform = 'scale(' + playback * 3 + ')'
}
start()
body {
overflow: hidden;
}
div {
position: absolute;
white-space: nowrap;
}
<div id="target">...HERE WE GO</div>
For Background Tasks (non-UI related)
对于后台任务(非 UI 相关)
@UpTheCreek comment:
@UpTheCreek 评论:
Fine for presentation issues, but still there are some things that you need to keep running.
对于演示问题很好,但仍有一些事情需要您继续运行。
If you have background tasks that needsto be precisely executed at given intervals, you can use HTML5 Web Workers. Take a look at M?hre's answer belowfor more details...
如果您有需要在给定时间间隔内精确执行的后台任务,您可以使用HTML5 Web Workers。看看下面 M?hre 的回答以了解更多细节......
CSS vs JS "animations"
CSS vs JS“动画”
This problem and many otherscould be avoided by using CSS transitions/animations instead of JavaScript based animations which adds a considerable overhead. I'd recommend this jQuery pluginthat let's you take benefit from CSS transitions just like the animate()
methods.
这个问题和许多其他问题可以通过使用 CSS 过渡/动画而不是基于 JavaScript 的动画来避免,这会增加相当大的开销。我会推荐这个jQuery 插件,它可以让你像animate()
方法一样从 CSS 转换中受益。
回答by M?hre
I ran into the same problem with audio fading and HTML5 player. It got stucked when tab became inactive. So I found out a WebWorker is allowed to use intervals/timeouts without limitation. I use it to post "ticks" to the main javascript.
我在音频淡入淡出和 HTML5 播放器方面遇到了同样的问题。当Tab变为不活动时,它被困扰着。所以我发现 WebWorker 可以无限制地使用间隔/超时。我用它来发布“滴答声”到主要的 javascript。
WebWorkers Code:
网络工作者代码:
var fading = false;
var interval;
self.addEventListener('message', function(e){
switch (e.data) {
case 'start':
if (!fading){
fading = true;
interval = setInterval(function(){
self.postMessage('tick');
}, 50);
}
break;
case 'stop':
clearInterval(interval);
fading = false;
break;
};
}, false);
Main Javascript:
主要Javascript:
var player = new Audio();
player.fader = new Worker('js/fader.js');
player.faderPosition = 0.0;
player.faderTargetVolume = 1.0;
player.faderCallback = function(){};
player.fadeTo = function(volume, func){
console.log('fadeTo called');
if (func) this.faderCallback = func;
this.faderTargetVolume = volume;
this.fader.postMessage('start');
}
player.fader.addEventListener('message', function(e){
console.log('fader tick');
if (player.faderTargetVolume > player.volume){
player.faderPosition -= 0.02;
} else {
player.faderPosition += 0.02;
}
var newVolume = Math.pow(player.faderPosition - 1, 2);
if (newVolume > 0.999){
player.volume = newVolume = 1.0;
player.fader.postMessage('stop');
player.faderCallback();
} else if (newVolume < 0.001) {
player.volume = newVolume = 0.0;
player.fader.postMessage('stop');
player.faderCallback();
} else {
player.volume = newVolume;
}
});
回答by Ruslan Tushov
There is a solution to use Web Workers (as mentioned before), because they run in separate process and are not slowed down
有一个使用 Web Workers 的解决方案(如前所述),因为它们在单独的进程中运行并且不会减慢
I've written a tiny script that can be used without changes to your code - it simply overrides functions setTimeout, clearTimeout, setInterval, clearInterval.
我编写了一个小脚本,无需更改代码即可使用 - 它只是覆盖了函数 setTimeout、clearTimeout、setInterval、clearInterval。
Just include it before all your code.
只需在所有代码之前包含它。
回答by www
Just do this:
只需这样做:
var $div = $('div');
var a = 0;
setInterval(function() {
a++;
$div.stop(true,true).css("left", a);
}, 1000 / 30);
Inactive browser tabs buffer some of the setInterval
or setTimeout
functions.
非活动浏览器选项卡会缓冲某些setInterval
或setTimeout
功能。
stop(true,true)
will stop all buffered events and execute immediatly only the last animation.
stop(true,true)
将停止所有缓冲事件并仅立即执行最后一个动画。
The window.setTimeout()
method now clamps to send no more than one timeout per second in inactive tabs. In addition, it now clamps nested timeouts to the smallest value allowed by the HTML5 specification: 4 ms (instead of the 10 ms it used to clamp to).
该window.setTimeout()
方法现在限制为在非活动选项卡中每秒发送不超过一个超时。此外,它现在将嵌套超时限制为 HTML5 规范允许的最小值:4 毫秒(而不是它过去用于限制的 10 毫秒)。
回答by 1st4ck
I think that a best understanding about this problem is in this example: http://jsfiddle.net/TAHDb/
我认为对这个问题最好的理解是在这个例子中:http: //jsfiddle.net/TAHDb/
I am doing a simple thing here:
我在这里做一件简单的事情:
Have a interval of 1 sec and each time hide the first span and move it to last, and show the 2nd span.
间隔 1 秒,每次隐藏第一个跨度并将其移至最后一个,并显示第二个跨度。
If you stay on page it works as it is supposed. But if you hide the tab for some seconds, when you get back you will see a weired thing.
如果您停留在页面上,它会按预期工作。但是如果你隐藏标签几秒钟,当你回来时你会看到一个奇怪的东西。
Its like all events that didn't ucur during the time you were inactive now will ocur all in 1 time. so for some few seconds you will get like X events. they are so quick that its possible to see all 6 spans at once.
就像所有在您不活动期间未发生的事件现在都将在 1 次发生。所以在几秒钟内你会得到像 X 个事件。它们是如此之快,以至于可以一次看到所有 6 个跨度。
So it seams chrome only delays the events, so when you get back all events will occur but all at once...
所以它接缝 chrome 只会延迟事件,所以当你回来时,所有事件都会发生,但一次......
A pratical application were this ocur iss for a simple slideshow. Imagine the numbers being Images, and if user stay with tab hidden when he came back he will see all imgs floating, Totally mesed.
一个实际的应用程序是一个简单的幻灯片。想象一下数字是图像,如果用户在回来时保持标签隐藏,他将看到所有 imgs 浮动,完全混乱。
To fix this use the stop(true,true) like pimvdb told. THis will clear the event queue.
要解决此问题,请使用像 pimvdb 所说的 stop(true,true) 。这将清除事件队列。
回答by Tomaaszq
For me it's not important to play audio in the background like for others here, my problem was that I had some animations and they acted like crazy when you were in other tabs and coming back to them. My solution was putting these animations inside ifthat is preventing inactive tab:
对我来说,像这里的其他人一样在后台播放音频并不重要,我的问题是我有一些动画,当你在其他选项卡中并回到它们时,它们表现得像疯了一样。如果阻止非活动选项卡,我的解决方案是将这些动画放入其中:
if (!document.hidden){ //your animation code here }
thanks to that my animation was running onlyif tab was active. I hope this will help someone with my case.
多亏了我的动画只有在选项卡处于活动状态时才运行。我希望这会对我的情况有所帮助。
回答by iman
Both setInterval
and requestAnimationFrame
don't work when tab is inactive or work but not at the right periods. A solution is to use another source for time events. For example web socketsor web workersare two event sources that work fine while tab is inactive. So no need to move all of your code to a web worker, just use worker as a time event source:
双方setInterval
并requestAnimationFrame
没有工作的时候标签是不活动或工作,但不是在正确的时间。一个解决方案是使用另一个时间事件源。例如,网络套接字或网络工作者是两个在选项卡处于非活动状态时工作正常的事件源。因此无需将所有代码都移至 Web Worker,只需将 Worker 用作时间事件源:
// worker.js
setInterval(function() {
postMessage('');
}, 1000 / 50);
.
.
var worker = new Worker('worker.js');
var t1 = 0;
worker.onmessage = function() {
var t2 = new Date().getTime();
console.log('fps =', 1000 / (t2 - t1) | 0);
t1 = t2;
}
jsfiddle linkof this sample.
此示例的jsfiddle 链接。
回答by Mariy
回答by Kaan Soral
Playing an audio file ensures full background Javascript performance for the time being
播放音频文件暂时确保完整的后台 Javascript 性能
For me, it was the simplest and least intrusive solution - apart from playing a faint / almost-empty sound, there are no other potential side effects
对我来说,这是最简单、干扰最少的解决方案——除了播放微弱/几乎是空的声音外,没有其他潜在的副作用
You can find the details here: https://stackoverflow.com/a/51191818/914546
您可以在此处找到详细信息:https: //stackoverflow.com/a/51191818/914546
(From other answers, I see that some people use different properties of the Audio tag, I do wonder whether it's possible to use the Audio tag for full performance, without actually playing something)
(从其他答案中,我看到有些人使用 Audio 标签的不同属性,我确实想知道是否可以使用 Audio 标签来获得完整性能,而无需实际播放某些内容)
回答by ashkan nasirzadeh
Note:this solution is not suitable if you like your interval works on the background, for example, playing audio or ... but if you are confused for example about your animation not working properly when coming back to your page(tab) this is a good solution.
注意:如果你喜欢你的间隔在背景上工作,这个解决方案不适合,例如,播放音频或......但如果你对返回页面(标签)时动画无法正常工作感到困惑,这是一个很好的解决方案。
There are many ways to achieve this goal, maybe the "WebWorkers" is the most standard one butcertainly, it's not the easiest and handy one, especially If you don't have enough Time, so you can try this way:
有很多方法可以实现这一目标,也许“WebWorkers”是最标准的一种,但当然,它不是最简单方便的一种,尤其是如果你没有足够的时间,那么你可以尝试这种方式:
?BASIC CONCEPT:
?基本概念:
1- build a name for your interval(or animation) and set your interval(animation), so it would run when user first time open your page : var interval_id = setInterval(your_func, 3000);
1-为您的间隔(或动画)建立一个名称并设置您的间隔(动画),以便在用户第一次打开您的页面时运行: var interval_id = setInterval(your_func, 3000);
2- by $(window).focus(function() {});
and $(window).blur(function() {});
you can clearInterval(interval_id)
everytime browser(tab) is deactived and ReRun your interval(animation) everytime browser(tab) would acive again by interval_id = setInterval();
2- 通过$(window).focus(function() {});
,$(window).blur(function() {});
您可以在clearInterval(interval_id)
每次浏览器(选项卡)停用时重新运行您的间隔(动画),每次浏览器(选项卡)再次激活时interval_id = setInterval();
?SAMPLE CODE:
?示例代码:
var interval_id = setInterval(your_func, 3000);
$(window).focus(function() {
interval_id = setInterval(your_func, 3000);
});
$(window).blur(function() {
clearInterval(interval_id);
interval_id = 0;
});