Search code examples
javascriptsettimeoutbrowser-extension

`setTimeout` working oddly in Firefox extension background.js


I'm making a Firefox browser extension (note: Chrome extensions and Firefox extensions have very similar APIs) that monitors screentime on certain websites that can be selected by the user. Specifically, the screen time counter should reset every 24 hours at midnight.

The code in question:

let interval = setInterval(update, 1000);

let msTillMidnight = (new Date().setHours(24, 0, 0, 0) - Date.now());

function restart() {
  browser.storage.local.get("totalTime").then(res => {
    browser.storage.local.set({timeLeft: res["totalTime"]});
  });
  if (!running) {
    running = true;
    interval = setInterval(update, 1000);
  }
}

new Promise(resolve => setTimeout(resolve, msTillMidnight)).then(() => { // I am aware this is laughably redundant, but in theory it should work exactly the same
  restart();
  setInterval(restart, 1000 * 60 * 60 * 24); // 1000 ms/second —> 60 s/minute —> 60 m/hour —> 24 h/day
});

I noticed a very odd behavior. After the first midnight after the extension began running, restart was not called. None of the browser storage vars were updated, nor the interval, not the running bool. The problem, I assumed, was that the setTimeout in question was simply not running. However, the next night, the clock had reset!

I don't understand. Is something wrong with my msTillMidnight variable? Is it because the background page shuts down and won't run any setTimeout scripts after a while when my computer is asleep at midnight and that the restart function will only call when my computer is still on or shut down recently? Or maybe, my redundant line of code that beats around the bush instead of a simple setTimeout is somehow causing a bug?

I would love to debug this on my own, but my testing cycles would most likely be 24 hours, which is a little slower than I would prefer...


Solution

  • setTimeout and setInterval don't rely on computer's clock. They are timers, so if browser "lost" some time due to computer sleep, the timers will not advance during that time.

    You'll need use setInterval and check date every nn seconds. This however might also fail if extension was suspended.

    A be better approach might be to use alarms instead. Specifically, by using when in alarmInfo option. However alarms will "catch up" if missed (for example in your case if computer was sleeping for 7 days, when woke up, it will trigger 7 alarms at once)

    This should work fine with manifest v2, however manifest v3 doesn't have background processes and it suspends extensions after 15 minutes or so, even alarms won't fire. There are a few work arounds this, none of which is perfect.