(I am paraphrasing question asked by Rich Harris in the "Stuff I wish I'd known sooner about service workers" gist.)
If I have code in my service worker that runs outside an event handler, when does it run?
And, closely related to that, what is the difference between putting inside an install
handler and putting it outside an event handler entirely?
In general, code that's outside any event handler, in the "top-level" of the service worker's global scope, will run each and every time the service worker thread(/process) is started up. The service worker thread may start (and stop) at arbitrary times, and it's not tied to the lifetime of the web pages it controlled.
(Starting/stopping the service worker thread frequently is a performance/battery optimization, and ensures that, e.g., just because you browse to a page that has registered a service worker, you won't get an extra idle thread spinning in the background.)
The flip side of that is that every time the service worker thread is stopped, any existing global state is destroyed. So while you can make certain optimizations, like storing an open IndexedDB connection in global state in the hopes of sharing it across multiple events, you need to be prepared to re-initialize them if the thread had been killed in between event handler invocations.
Closely related to this question is a misconception I've seen about the
install
event handler. I have seen some developers use theinstall
handler to initialize global state that they then rely on in other event handlers, likefetch
. This is dangerous, and will likely lead to bugs in production. Theinstall
handler fires once per version of a service worker, and is normally best used for tasks that are tied to service worker versioning—like caching new or updated resources that are needed by that version. After theinstall
handler has completed successfully, a given version of a service worker will be considered "installed", and theinstall
handler won't be triggered again when the service worker starts up to handle, e.g., afetch
ormessage
event.So, if there is global state that needs to be initialized prior to handling, e.g., a
fetch
event, you can do that in the top-level service worker global scope (optionally waiting on a promise to resolve inside thefetch
event handler to ensure that any asynchronous operations have completed). Do not rely on theinstall
handler to set up global scope!Here's an example that illustrates some of these points: