How do I call a function periodically in a manifest v3 Chrome extension?

Viewed 2286

Service workers replace background pages in manifest v3 chrome extensions, and I'm trying to use one for my extension. I need to be able to run a function periodically, and it looks like alarms are the way to go. In the example they recommend doing this in the top level of the service worker:

chrome.alarms.create({ delayInMinutes: 3.0 });

chrome.alarms.onAlarm.addListener(() => {
  chrome.action.setIcon({
    path: getRandomIconPath(),
  });
});

From what I understand though, weather or not my service worker gets killed in between events is non-deterministic. I believe if the browser kills my service worker this will be called every 3 minutes, since when the script is re-started to handle the alarm, it will run the first line again and queue up another alarm.

By contrast, if the browser lets my service worker live for the 3 minutes in between alarms this won't loop, because it will only call addListener() once, but will call the callback twice (once for the first alarm that originally spawned this service worker, and again for the alarm registered on the first line of this invocation of the service worker). The service worker will then eventually die and no alarm will ever wake it again.

Am I misunderstanding how events work here? If not, how do I register a re-occurring alarm once chrome.alarms.create({ periodInMinutes: 3.0 }); and avoid re-registering it every time my service worker is re-started?

NOTE: delayInMinutes fires once, periodInMinutes is re-ocuring.

1 Answers

Although it's non-deterministic but the rules are quite simple and the worker behaves almost exactly like the old event page of ManifestV2, the major points are:

  • it wakes up when a registered API event occurs
  • it runs the entire script so the API listeners are re-registered
  • it runs the listener for the API event that woke it
  • it unloads after 30 seconds (15 in MV2) after the last API event; if another API event is triggered the unload timer is restarted; the timer will be extended by five minutes if there is an open port for chrome.runtime messaging, in MV3 the messaging ports are force-disconnected after five minutes whereas in MV2 these ports never force-disconnect and thus keep the MV2 event page alive.

Now back to that demo script. You're right: it relies on non-deterministic behavior. It's just bad like many examples in the documentation for extensions. Use API reference and devtools debugger.

A more realistic example would be to register an alarm just once, for example in chrome.runtime.onInstalled event, because the alarms are remembered by the browser internally. Also, it's best to give it an id so we can check for its existence:

chrome.runtime.onInstalled.addListener(() => {
  chrome.alarms.get('periodic', a => {
    if (!a) chrome.alarms.create('periodic', { periodInMinutes: 3.0 });
  });
});

chrome.alarms.onAlarm.addListener(() => {
  chrome.action.setIcon({
    path: getRandomIconPath(),
  });
});
Related