Chrome: Timeouts/Interval Suspended in Background Tabs

Chrome: timeouts/interval suspended in background tabs?

I recently asked about this and it is behaviour by design. When a tab is inactive, only at a maximum of once per second the function is called. Here is the code change.

Perhaps this will help:
How can I make setInterval also work when a tab is inactive in Chrome?

TL;DR: use Web Workers.

Minimum setInterval()/setTimeout() delay on background tabs

I know your example was for Chrome, but for Firefox at least, from the Firefox 5 Release Notes (under "What's New in Firefox").

Background tabs have setTimeout and
setInterval clamped to 1000ms to
improve performance


Oh, and I just realised this has already been asked in regard to chrome, over here: Chrome: timeouts/interval suspended in background tabs?. There's a link there that shows that this is also true in Chrome.

Javascript SetInterval execution delay in while switch browser tab

When a tab is not active, the function can be called at a maximum of one per second. One option to solve this is to use Web Workers, because they run in separate process, hence are not slowed down.

Another option to solve this issue you can a hack by playing an ~empty sound which will force the browser to keep the regular performance.

More about this inactivation issue - https://codereview.chromium.org/6577021

Empty sound hack - How to make JavaScript run at normal speed in Chrome even when tab is not active?

How can I make setInterval also work when a tab is inactive in Chrome?

On most browsers inactive tabs have low priority execution and this can affect JavaScript timers.

If the values of your transition were calculated using real time elapsed between frames instead fixed increments on each interval, you not only workaround this issue but also can achieve a smother animation by using requestAnimationFrame as it can get up to 60fps if the processor isn't very busy.

Here's a vanilla JavaScript example of an animated property transition using 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>

How to prevent timer from being throttled by Chrome when my webpage in background?

You could try loading the script when the window is active, but if it can't be helped, HackTimer.js is a good workaround using Web Workers.

Chrome: timeouts/interval suspended in background tabs?

I recently asked about this and it is behaviour by design. When a tab is inactive, only at a maximum of once per second the function is called. Here is the code change.

Perhaps this will help:
How can I make setInterval also work when a tab is inactive in Chrome?

TL;DR: use Web Workers.

setTimeout/setInterval 1000ms lag in background tabs (Chrome and Firefox)

EDIT: Another answer is to use WebWorkers per https://stackoverflow.com/a/12522580/1481489 - this answer is a little specific, so here's something more generic:

interval.js

var intervalId = null;
onmessage = function(event) {
if ( event.data.start ) {
intervalId = setInterval(function(){
postMessage('interval.start');
},event.data.ms||0);
}
if ( event.data.stop && intervalId !== null ) {
clearInterval(intervalId);
}
};

and your main program:

var stuff = { // your custom class or object or whatever...
first: Date.now(),
last: Date.now(),
callback: function callback() {
var cur = Date.now();
document.title = ((cur-this.last)/1000).toString()+' | '+((cur-this.first)/1000).toString();
this.last = cur;
}
};

var doWork = new Worker('interval.js');
doWork.onmessage = function(event) {
if ( event.data === 'interval.start' ) {
stuff.callback(); // queue your custom methods in here or whatever
}
};
doWork.postMessage({start:true,ms:250}); // tell the worker to start up with 250ms intervals
// doWork.postMessage({stop:true}); // or tell it just to stop.

Totally ugly, but you could open up a child popup window. However, all this does is transfer some of the caveats to the child window, i.e. if child window is minimized the 1000ms problem appears, but if it is simply out of focus, there isn't an issue. Then again, if it is closed, then it stops, but all the user has to do is click the start button again.

So, I suppose this doesn't really solve your problem... but here's a rough draft:

var mainIntervalMs = 250;

var stuff = { // your custom class or object or whatever...
first: Date.now(),
last: Date.now(),
callback: function callback(){
var cur = Date.now();
document.title = ((cur-this.last)/1000).toString()+' | '+((cur-this.first)/1000).toString();
this.last = cur;
}
};

function openerCallbackHandler() {
stuff.callback(); // queue your custom methods in here or whatever
}

function openerTick(childIntervalMs) { // this isn't actually used in this window, but makes it easier to embed the code in the child window
setInterval(function() {
window.opener.openerCallbackHandler();
},childIntervalMs);
}

// build the popup that will handle the interval
function buildIntervalWindow() {
var controlWindow = window.open('about:blank','controlWindow','width=10,height=10');
var script = controlWindow.document.createElement('script');
script.type = 'text/javascript';
script.textContent = '('+openerTick+')('+mainIntervalMs+');';
controlWindow.document.body.appendChild(script);
}

// write the start button to circumvent popup blockers
document.write('<input type="button" onclick="buildIntervalWindow();return false;" value="Start" />');

I'd recommend working out a better way to organize, write, etc. but at the least it should point you in the right direction. It should also work in a lot of diff browsers (in theory, only tested in chrome). I'll leave you to the rest.

Oh, and don't forget to build in auto-closing of the child window if the parent drops.



Related Topics



Leave a reply



Submit