What Is a Valid Event for a Service Worker to Emit When Its Registered?

The service worker lifecycle

— Updated

The lifecycle of the service worker is its most complicated part. If you lot don't know what information technology'southward trying to do and what the benefits are, it can feel similar it's fighting yous. But in one case you lot know how it works, you lot tin can evangelize seamless, unobtrusive updates to users, mixing the best of web and native patterns.

This is a deep dive, simply the bullets at the start of each section cover most of what y'all need to know.

The intent #

The intent of the lifecycle is to:

  • Make offline-offset possible.
  • Allow a new service worker to get itself ready without disrupting the current 1.
  • Ensure an in-scope folio is controlled by the same service worker (or no service worker) throughout.
  • Ensure at that place'south just one version of your site running at once.

That last one is pretty important. Without service workers, users tin load ane tab to your site, and then after open some other. This can result in two versions of your site running at the aforementioned fourth dimension. Sometimes this is ok, merely if you're dealing with storage yous tin can easily cease up with two tabs having very different opinions on how their shared storage should be managed. This can result in errors, or worse, data loss.

The first service worker #

In brief:

  • The install upshot is the first result a service worker gets, and information technology simply happens once.
  • A promise passed to installEvent.waitUntil() signals the duration and success or failure of your install.
  • A service worker won't receive events like fetch and push until information technology successfully finishes installing and becomes "active".
  • By default, a folio'south fetches won't get through a service worker unless the folio asking itself went through a service worker. So you'll need to refresh the page to meet the effects of the service worker.
  • clients.claim() can override this default, and take command of not-controlled pages.

Take this HTML:

                                          <!                DOCTYPE                html                >                            
An image will appear here in 3 seconds:
<script >
navigator.serviceWorker. register ( '/sw.js' )
. and so ( reg => console. log ( 'SW registered!' , reg) )
. catch ( err => console. log ( 'Boo!' , err) ) ;

setTimeout ( ( ) => {
const img = new Paradigm ( ) ;
img.src = '/dog.svg' ;
document.body. appendChild (img) ;
} , 3000 ) ;

</script >

It registers a service worker, and adds image of a dog after 3 seconds.

Hither's its service worker, sw.js:

            self.              addEventListener              (              'install'              ,              event              =>              {              
console. log ( 'V1 installing…' ) ;

// cache a cat SVG
event. waitUntil (
caches. open ( 'static-v1' ) . then ( cache => cache. add ( '/cat.svg' ) )
) ;
} ) ;

self. addEventListener ( 'actuate' , outcome => {
panel. log ( 'V1 now ready to handle fetches!' ) ;
} ) ;

self. addEventListener ( 'fetch' , result => {
const url = new URL (event.request.url) ;

// serve the cat SVG from the enshroud if the request is
// same-origin and the path is '/canis familiaris.svg'
if (url.origin == location.origin && url.pathname == '/domestic dog.svg' ) {
event. respondWith (caches. match ( '/cat.svg' ) ) ;
}
} ) ;

Information technology caches an image of a cat, and serves it whenever at that place's a request for /dog.svg. All the same, if you run the higher up case, yous'll see a dog the beginning fourth dimension you load the page. Striking refresh, and you'll see the cat.

Note: Cats are better than dogs. They only are.

Scope and control #

The default scope of a service worker registration is ./ relative to the script URL. This means if you register a service worker at //example.com/foo/bar.js it has a default scope of //example.com/foo/.

Nosotros call pages, workers, and shared workers clients. Your service worker can only control clients that are in-scope. One time a client is "controlled", its fetches go through the in-scope service worker. You can detect if a customer is controlled via navigator.serviceWorker.controller which will be null or a service worker instance.

Download, parse, and execute #

Your very beginning service worker downloads when y'all call .register(). If your script fails to download, parse, or throws an error in its initial execution, the register promise rejects, and the service worker is discarded.

Chrome's DevTools shows the error in the console, and in the service worker section of the application tab:

Error displayed in service worker DevTools tab

Install #

The first outcome a service worker gets is install. It's triggered every bit presently as the worker executes, and it's only called one time per service worker. If yous alter your service worker script the browser considers it a different service worker, and information technology'll get its own install event. I'll cover updates in particular after.

The install event is your chance to cache everything you need before existence able to command clients. The hope you laissez passer to event.waitUntil() lets the browser know when your install completes, and if it was successful.

If your promise rejects, this signals the install failed, and the browser throws the service worker away. It'll never control clients. This means we can't rely on cat.svg beingness nowadays in the cache in our fetch events. It'south a dependency.

Activate #

Once your service worker is set to control clients and handle functional events like push and sync, you lot'll get an activate event. But that doesn't mean the folio that called .register() will be controlled.

The showtime fourth dimension you load the demo, even though canis familiaris.svg is requested long subsequently the service worker activates, it doesn't handle the request, and you still run across the prototype of the dog. The default is consistency, if your page loads without a service worker, neither will its subresources. If you load the demo a second time (in other words, refresh the page), it'll be controlled. Both the folio and the paradigm volition get through fetch events, and you'll see a true cat instead.

clients.merits #

You tin take control of uncontrolled clients past calling clients.claim() within your service worker once information technology's activated.

Here's a variation of the demo above which calls clients.claim() in its activate upshot. You should see a cat the first time. I say "should", because this is timing sensitive. You'll simply see a cat if the service worker activates and clients.merits() takes effect before the image tries to load.

If you use your service worker to load pages differently than they'd load via the network, clients.merits() tin be troublesome, equally your service worker ends up controlling some clients that loaded without information technology.

Updating the service worker #

In brief:

  • An update is triggered if any of the following happens:
    • A navigation to an in-scope page.
    • A functional events such as push and sync, unless there'south been an update bank check within the previous 24 hours.
    • Calling .register() only if the service worker URL has changed. However, you should avoid changing the worker URL.
  • Most browsers, including Chrome 68 and afterward, default to ignoring caching headers when checking for updates of the registered service worker script. They still respect caching headers when fetching resources loaded inside a service worker via importScripts(). Yous can override this default beliefs past setting the updateViaCache selection when registering your service worker.
  • Your service worker is considered updated if it's byte-different to the one the browser already has. (We're extending this to include imported scripts/modules too.)
  • The updated service worker is launched alongside the existing ane, and gets its ain install consequence.
  • If your new worker has a not-ok condition code (for example, 404), fails to parse, throws an error during execution, or rejects during install, the new worker is thrown away, but the current i remains active.
  • Once successfully installed, the updated worker will await until the existing worker is controlling aught clients. (Note that clients overlap during a refresh.)
  • cocky.skipWaiting() prevents the waiting, pregnant the service worker activates as soon equally information technology's finished installing.

Let's say we changed our service worker script to respond with a movie of a equus caballus rather than a cat:

                          const              expectedCaches              =              [              'static-v2'              ]              ;              

self. addEventListener ( 'install' , event => {
console. log ( 'V2 installing…' ) ;

// cache a horse SVG into a new enshroud, static-v2
event. waitUntil (
caches. open ( 'static-v2' ) . so ( cache => enshroud. add together ( '/horse.svg' ) )
) ;
} ) ;

self. addEventListener ( 'activate' , event => {
// delete any caches that aren't in expectedCaches
// which will get rid of static-v1
upshot. waitUntil (
caches. keys ( ) . then ( keys => Promise. all (
keys. map ( key => {
if ( !expectedCaches. includes (key) ) {
render caches. delete (cardinal) ;
}
} )
) ) . and so ( ( ) => {
console. log ( 'V2 now ready to handle fetches!' ) ;
} )
) ;
} ) ;

self. addEventListener ( 'fetch' , event => {
const url = new URL (event.asking.url) ;

// serve the horse SVG from the cache if the request is
// same-origin and the path is '/dog.svg'
if (url.origin == location.origin && url.pathname == '/dog.svg' ) {
event. respondWith (caches. match ( '/horse.svg' ) ) ;
}
} ) ;

Cheque out a demo of the above. You should even so see an image of a true cat. Here'due south why…

Install #

Notation that I've inverse the cache proper name from static-v1 to static-v2. This ways I can gear up the new cache without overwriting things in the current one, which the old service worker is still using.

This patterns creates version-specific caches, akin to assets a native app would bundle with its executable. You may also take caches that aren't version specific, such as avatars.

Waiting #

After it's successfully installed, the updated service worker delays activating until the existing service worker is no longer controlling clients. This state is called "waiting", and information technology's how the browser ensures that only one version of your service worker is running at a time.

If you ran the updated demo, yous should nevertheless run across a picture of a cat, because the V2 worker hasn't yet activated. You can meet the new service worker waiting in the "Application" tab of DevTools:

DevTools showing new service worker waiting

Even if you only take i tab open to the demo, refreshing the page isn't enough to let the new version take over. This is due to how browser navigations work. When you navigate, the electric current page doesn't go away until the response headers have been received, and even so the current page may stay if the response has a Content-Disposition header. Because of this overlap, the electric current service worker is always controlling a client during a refresh.

To get the update, close or navigate abroad from all tabs using the current service worker. Then, when you navigate to the demo again, you should come across the horse.

This pattern is similar to how Chrome updates. Updates to Chrome download in the groundwork, but don't apply until Chrome restarts. In the mean time, you can keep to use the current version without disruption. Notwithstanding, this is a pain during development, but DevTools has ways to make it easier, which I'll cover later in this commodity.

Activate #

This fires one time the sometime service worker is gone, and your new service worker is able to control clients. This is the platonic fourth dimension to exercise stuff that you couldn't exercise while the one-time worker was still in apply, such as migrating databases and clearing caches.

In the demo higher up, I maintain a listing of caches that I await to be there, and in the activate outcome I become rid of any others, which removes the sometime static-v1 cache.

If you lot pass a promise to result.waitUntil() it'll buffer functional events (fetch, push, sync etc.) until the hope resolves. So when your fetch event fires, the activation is fully complete.

Skip the waiting stage #

The waiting phase means you're merely running one version of your site at once, but if yous don't need that feature, you lot can make your new service worker actuate sooner past calling self.skipWaiting().

This causes your service worker to boot out the current agile worker and actuate itself as shortly every bit it enters the waiting phase (or immediately if information technology's already in the waiting phase). It doesn't crusade your worker to skip installing, just waiting.

It doesn't really matter when yous call skipWaiting(), as long as it's during or before waiting. Information technology's pretty mutual to call it in the install effect:

            self.              addEventListener              (              'install'              ,              event              =>              {              
cocky. skipWaiting ( ) ;

issue. waitUntil (
// caching etc
) ;
} ) ;

Only you lot may want to call it every bit a results of a postMessage() to the service worker. Every bit in, you desire to skipWaiting() following a user interaction.

Here's a demo that uses skipWaiting(). Yous should see a picture of a cow without having to navigate away. Like clients.claim() it'south a race, so you'll only see the moo-cow if the new service worker fetches, installs and activates before the page tries to load the image.

Manual updates #

As I mentioned earlier, the browser checks for updates automatically after navigations and functional events, but y'all can as well trigger them manually:

            navigator.serviceWorker.              register              (              '/sw.js'              )              .              then              (              reg              =>              {              
// sometime later…
reg. update ( ) ;
} ) ;

If yous await the user to be using your site for a long time without reloading, you may want to call update() on an interval (such as hourly).

Avoid changing the URL of your service worker script #

If you've read my mail service on caching best practices, yous may consider giving each version of your service worker a unique URL. Don't do this! This is usually bad exercise for service workers, just update the script at its electric current location.

Information technology can state you lot with a problem like this:

  1. index.html registers sw-v1.js equally a service worker.
  2. sw-v1.js caches and serves index.html so it works offline-commencement.
  3. You update index.html so it registers your new and shiny sw-v2.js.

If y'all do the above, the user never gets sw-v2.js, considering sw-v1.js is serving the old version of alphabetize.html from its cache. Y'all've put yourself in a position where you need to update your service worker in order to update your service worker. Ew.

However, for the demo above, I have changed the URL of the service worker. This is and so, for the sake of the demo, you can switch between the versions. Information technology isn't something I'd do in product.

The service worker lifecycle is built with the user in mind, merely during evolution information technology's a flake of a pain. Thankfully there are a few tools to help out:

Update on reload #

This ane's my favourite.

DevTools showing 'update on reload'

This changes the lifecycle to be programmer-friendly. Each navigation will:

  1. Refetch the service worker.
  2. Install information technology equally a new version even if it's byte-identical, pregnant your install effect runs and your caches update.
  3. Skip the waiting phase so the new service worker activates.
  4. Navigate the page.

This means you'll go your updates on each navigation (including refresh) without having to reload twice or close the tab.

Skip waiting #

DevTools showing 'skip waiting'

If you take a worker waiting, you can hit "skip waiting" in DevTools to immediately promote it to "active".

Shift-reload #

If you force-reload the page (shift-reload) information technology bypasses the service worker entirely. It'll be uncontrolled. This feature is in the spec, so it works in other service-worker-supporting browsers.

Handling updates #

The service worker was designed as part of the extensible web. The idea is that nosotros, every bit browser developers, admit that nosotros are not better at spider web evolution than spider web developers. And as such, we shouldn't provide narrow loftier-level APIs that solve a detail problem using patterns we like, and instead give yous access to the guts of the browser and permit y'all do information technology how you want, in a way that works best for your users.

So, to enable as many patterns every bit nosotros can, the whole update bicycle is observable:

            navigator.serviceWorker.              register              (              '/sw.js'              )              .              then              (              reg              =>              {              
reg.installing; // the installing worker, or undefined
reg.waiting; // the waiting worker, or undefined
reg.agile; // the agile worker, or undefined

reg. addEventListener ( 'updatefound' , ( ) => {
// A wild service worker has appeared in reg.installing!
const newWorker = reg.installing;

newWorker.land;
// "installing" - the install event has fired, but non yet complete
// "installed" - install consummate
// "activating" - the activate upshot has fired, but not yet complete
// "activated" - fully active
// "redundant" - discarded. Either failed install, or it's been
// replaced by a newer version

newWorker. addEventListener ( 'statechange' , ( ) => {
// newWorker.state has changed
} ) ;
} ) ;
} ) ;

navigator.serviceWorker. addEventListener ( 'controllerchange' , ( ) => {
// This fires when the service worker decision-making this page
// changes, eg a new worker has skipped waiting and become
// the new agile worker.
} ) ;

The lifecycle goes ever on #

Every bit you tin see, it pays to sympathise the service worker lifecycle—and with that understanding, service worker behaviors should seem more than logical, and less mysterious. That knowledge will give you more than confidence every bit you deploy and update service workers.

Last updated: — Better article

Return to all articles

heaneputted1955.blogspot.com

Source: https://web.dev/service-worker-lifecycle/

0 Response to "What Is a Valid Event for a Service Worker to Emit When Its Registered?"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel