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

后端 未结 13 1949
被撕碎了的回忆
被撕碎了的回忆 2020-11-21 09:59

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 inacti

相关标签:
13条回答
  • 2020-11-21 10:28

    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>


    For Background Tasks (non-UI related)

    @UpTheCreek comment:

    Fine for presentation issues, but still there are some things that you need to keep running.

    If you have background tasks that needs to be precisely executed at given intervals, you can use HTML5 Web Workers. Take a look at Möhre's answer below for more details...

    CSS vs JS "animations"

    This problem and many others could be avoided by using CSS transitions/animations instead of JavaScript based animations which adds a considerable overhead. I'd recommend this jQuery plugin that let's you take benefit from CSS transitions just like the animate() methods.

    0 讨论(0)
  • 2020-11-21 10:30

    I think that a best understanding about this problem is in this example: 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.

    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.

    So it seams chrome only delays the events, so when you get back all events will occur but all at once...

    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.

    To fix this use the stop(true,true) like pimvdb told. THis will clear the event queue.

    0 讨论(0)
  • 2020-11-21 10:33

    Playing an audio file ensures full background Javascript performance for the time being

    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

    (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)

    0 讨论(0)
  • 2020-11-21 10:35

    Here's my rough solution

    (function(){
    var index = 1;
    var intervals = {},
        timeouts = {};
    
    function postMessageHandler(e) {
        window.postMessage('', "*");
    
        var now = new Date().getTime();
    
        sysFunc._each.call(timeouts, function(ind, obj) {
            var targetTime = obj[1];
    
            if (now >= targetTime) {
                obj[0]();
                delete timeouts[ind];
            }
        });
        sysFunc._each.call(intervals, function(ind, obj) {
            var startTime = obj[1];
            var func = obj[0];
            var ms = obj[2];
    
            if (now >= startTime + ms) {
                func();
                obj[1] = new Date().getTime();
            }
        });
    }
    window.addEventListener("message", postMessageHandler, true);
    window.postMessage('', "*");
    
    function _setTimeout(func, ms) {
        timeouts[index] = [func, new Date().getTime() + ms];
        return index++;
    }
    
    function _setInterval(func, ms) {
        intervals[index] = [func, new Date().getTime(), ms];
        return index++;
    }
    
    function _clearInterval(ind) {
        if (intervals[ind]) {
            delete intervals[ind]
        }
    }
    function _clearTimeout(ind) {
        if (timeouts[ind]) {
            delete timeouts[ind]
        }
    }
    
    var intervalIndex = _setInterval(function() {
        console.log('every 100ms');
    }, 100);
    _setTimeout(function() {
        console.log('run after 200ms');
    }, 200);
    _setTimeout(function() {
        console.log('closing the one that\'s 100ms');
        _clearInterval(intervalIndex)
    }, 2000);
    
    window._setTimeout = _setTimeout;
    window._setInterval = _setInterval;
    window._clearTimeout = _clearTimeout;
    window._clearInterval = _clearInterval;
    })();
    
    0 讨论(0)
  • 2020-11-21 10:37

    I ran into the same problem with audio fading and HTML5 player. It got stuck 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.

    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:

    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;
        }
    });
    
    0 讨论(0)
  • 2020-11-21 10:38

    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 if that is preventing inactive tab:

    if (!document.hidden){ //your animation code here }
    

    thanks to that my animation was running only if tab was active. I hope this will help someone with my case.

    0 讨论(0)
提交回复
热议问题