1

I have a bit of code that gets looped and interrupted for a few seconds specified in a setTimeout function, then the code is looped again once the delay is up. The problem that I'm facing is that when the page loses focus for a while, the timeout gets out of sync and the code is looped at "random" intervals other than what's specified in the setTimeout delay.

Here's the current code for reference:

$(document).ready(function () {
    for (var key in keys) {
        element = key;
        val = keys[key];
        test(element, val);
        (function loop(element, val) {
            setTimeout(function () {
                test(element, val);
                loop(element, val);
            }, 5000);
        })(element, val);
    }
});

I've searched around a bit and the issue seems to be expected and I'm trying to find the ideal workaround, thus far I've gone with the simpler approach which is to reload the entire page when it loses and then regains focus. It works but I'm not super happy with it.

Here's the current workaround:

$(window).blur(function(){
    $(window).focus(function(){
        location.reload();
    });
});

I've also read about using clearTimeout with focus in an attempt to restart or sync the timeout but during many attempts I wasn't able to get something working properly. At best it would still throw the timeout out of whack in some form or another.

I'm at loss about which direction to take in order to get a more suitable approach than to refresh the webpage.

Thanks in advance for any suggestion.

ner0
  • 84
  • 1
  • 9

2 Answers2

0

I don't think this will be the best performing solution.

But you could set the deadline. Using setInterval to check every second if the deadline has reached.

const finish = Date.now() + 10e3;

function test(timeout) {
  console.log('Start: ', new Date().toGMTString());

  let timer = setInterval(() => {
    if (Date.now() >= timeout) {
      console.log('Finished: ', new Date(timeout).toGMTString());
      clearInterval(timer);
    }
  }, 1000);
}

test(finish);
a.mola
  • 3,883
  • 7
  • 23
  • Not sure if I applied this correctly, but it ended out of sync in a similar fashion. The time between loops breaks/desyncs. Loops start getting gaps of 1s between them instead of running consecutively. Like it takes 10 seconds to loop through all the keys instead of 1/2 second. – ner0 Sep 12 '21 at 22:24
  • Did you try reducing the interval of `1000ms` to something smaller? Maybe like `500ms` or `100ms`. That should help the interval check when it goes out of sync. – a.mola Sep 13 '21 at 08:47
  • No, I did not test a shorter interval. I appreciate your suggestion in any case. For now I'm going to use the workaround that I posted which in my use case fits pretty well since I don't need to have the function running when the user is not "looking". – ner0 Sep 13 '21 at 21:22
0

I found a suitable answer in another question which works fine for my use case.

The suggestion is quite simple: avoid running code that depends on timed or throttled functions when the tab/page is not focused or, more accurately, when it is hidden from the user:

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

source: https://stackoverflow.com/a/53944438/924855

Although my issue is not related to animation, there is a commonality which is suffering from the throttling of setTimeout when the page is not in view. Using this approach I avoid running code entirely when the page is not visible and it avoids de-syncing altogether, like so:

$(document).ready(function () {
    for (var key in keys) {
        element = key;
        val = keys[key];
        test(element, val);
        (function loop(element, val) {
            setTimeout(function () {
                if (!document.hidden) {
                    test(element, val);
                }
                loop(element, val);
            }, 5000);
        })(element, val);
    }
});
ner0
  • 84
  • 1
  • 9