What is a Service Worker? Transform Your Web Site to an Instant Loading Powerhouse!
Trying to figure out what a service worker is? You're not alone!
A Service Worker is a script that executes in the background, in a separate thread from the browser UI. Service worker cache makes it possible for a web site to function offline. They are the technical powerhouse that levels up a website to a progressive web app. They enable deep platform integration, like rich caching, push notifications and background sync.
Service workers are designed to be an extensible platform, additional features are currently being planned.
They can't access the DOM, but can intercept all network request. This allows developers the opportunity to control how requests are handled, providing a rich way to make websites work offline.
Service workers are have been called a game changer for the web. I don't think that is a simple exaggeration, because they enable many much needed capabilities and make the core architecture I used for years native.
Service Workers sound amazing!
This is the key technology or modern web API behind Progressive Web Applications. Without a service worker a website is just a website, add a service worker and you now have an application.
There are some key features about service workers you need to understand:
- A Service Worker is a JavaScript File
- They execute on a separate thread from the UI
- They cannot access the DOM directly
- There is a life cycle or series of events a service worker flows through to become active, explained later
- They are only live while being used, so no battery strain
- They are isolated to the origin or domain they are registered with
- Service Workers require HTTPS
- Can send messages to and from the UI
- Do not require an open web page to function
- Are supported by all major browsers, including iOS Safari
- Are similar to Web Workers, but better in many ways
- How Do Service Workers Work?
- Service Worker Extensibility
- Service Worker Life Cycle
- How Long to Service Workers Last?
- How do I Check if a Service Worker is Registered?
- How do I Unregister a Service Worker
- Service Worker Caching
- How do I Update My Service Worker Cache
- What Browsers Support Service Workers?
- What is the Difference Between a Service Worker and a Web Worker
- What a Service Worker Can't Do
How Do Service Workers Work?
A Service worker sits between the browser and the network, acting like a proxy server, handling a collection of non-UI centric tasks. They are event driven and live outside the browser process, enabling them to work without an active browser session. The service worker is a script that executes in a thread, separate from the UI. This enables the service worker to perform non-UI tasks, making a web site perform better.
Service workers terminate when not in use and restored when required, this keeps them from straining the CPU and draining batteries. They act as a programmable network proxy, or intermediary between the browser and the network, empowering developers to design how network requests are handled.
The first power a service worker brings to a web site is the ability to enable offline capabilities with granular control. This is done with a rich caching API and intercepting all network requests before they leave.
Caching not only enables offline experiences, websites can load instantly when retrieved from cache. Service worker caching makes the network a progressive enhancement, or not required to make the site usable.
Service Worker Extensibility
An underestimated service worker feature is its extensibility Service workers are designed to be the backbone that supports additional functionality. The first two features shipping in browsers are native push notifications and background sync. More new APIs are currently being debated and should start appearing in browsers in the near future.
Service workers live in their own thread and do not have access to the DOM. They also have their own global scope, referred to using the 'self' object.
The also require a site is served using HTTPS. This requirement is due to the powerful features service workers offer. HTTPS prevents many common attacks. Today all sites should be served using HTTPS as the barriers to implement have been lifted and the minimal amount of security they offer.
They are also asynchronous. This means all APIs support promises. This also means certain APIs and functions are not accessible in service workers. The most notable is localStorage. Instead, data should be stored using IndexedDB.
Service Worker Life Cycle
Before diving into these great service worker features developers need to understand the life cycle.
A service worker must be registered by a website. Because some browsers still don't support service workers you should perform a feature check before registering a service worker. This is done by checking for the presence of 'serviceWorker' in navigator.
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(function(registration) {
// Registration was successful
})
.catch(function(err) {
// registration failed :(
});
}
To register the service worker call navigator.serviceWorker.register. Pass the path to the service worker. The register function returns a promise.
If successful the promise returns a reference to the service worker registration object. From there you can perform specific UI tasks as needed.
Standard Service Worker Events
- install
- activate
- fetch
- push (not supported by Safari)
- sync (not supported by most browsers yet)
When a service worker is registered the "install" event. In this event the most common task to perform is called pre-caching.
Pre-caching is where a pre-defined list of assets files needed to form a page or site, and add them to cache before they are requested. This takes advantage of the service worker cache to persist these responses.
self.addEventListener("install", event => {
event.waitUntil(
caches.open(preCacheName)
.then(function (cache) {
return cache.addAll(precache_urls);
})
);
});
The install event is only triggered once in a service worker's life time. The event will not trigger again until the service worker is updated.
The next event that fires as part of the registration life cycle is 'activate'. When a service worker is installed it does not immediately become active. The natural sequence is for a new service worker to wait until all service worker 'clients' are closed.
The reason service workers are designed not to immediately take over is to keep them from breaking the user experience.
The skipWaiting function forces a waiting service worker to become active. This should only be employed when you are certain the new service worker will not break any existing clients.
self.skipWaiting();
Once the service worker becomes active the 'activate' event fires.
self.addEventListener("activate", event => {
//on activate
event.waitUntil(clients.claim());
});
This event is commonly used to perform any clean up or migration tasks. For example, removing legacy caches that might conflict with the new service worker caches.
How Long to Service Workers Last?
There is no hard rule as to how long a browser keeps a service worker running. Internally the browser's engine will use a set of heuristics to determine when to terminate the service worker process.
In general if a web page is dormant the process will spin down after a minute or two, maybe as soon as 30 seconds.
The reality is the exact time will depend on hardware, usage patterns, etc.
The good news is it takes a handful of milliseconds to turn a service worker on. It is so fast you wont really notice any latency. You also do not need to program anything special to deal with a scenario without the service worker, you site will just work. The magic of progressive enhancement.
How do I Check if a Service Worker is Registered?
There are multiple ways to check if a service worker is registered. The easiest is to call the serviceworkers.getRegistrations method.
navigator.serviceWorker.getRegistrations().then(registrations => {
console.log(registrations);
});
This is code you can add to your pages script to log registration for technical support or developer debugging.
The 'getRegistrations' method returns an array of all service workers registered under the current scope or website.
If you are just logging the registration to the console you would be better off viewing the Dev Tools 'Application' tab. It has a sub-panel that displays a visual of the origin's registered service worker. You can also trigger background sync events and test push notifications.
You can also delete the service worker, force an update and other general management that helps with development.
The real value getRegistrations offers is for some sort of back-door support feature. We all know what it is like trying to help someone remotely and you cannot access the DevTools to really troubleshoot the problem. Sometimes you will want some sort of visual support page or modal to provide information about the application state.
The average consumer will not know how to access the DevTools console panel. Instead you can create a user interface within the application to echo the service worker registration object properties.
How do I Unregister a Service Worker
Hopefully you will not encounter a scenario where your service worker code has create a user experience bug. In case you do there are few things you can do to remove or unregister a service worker.
Most scenarios where you need to remove a service worker will be when you are developing the application. In this case you can use the DevTools to remove or delete the service worker.
If you need to remove or delete a deployed service worker it gets a little more tricky. There are programmatic ways to get you out of the jam.
Read more about removal techniques.
Service Worker Caching
The service worker specification includes native caching capabilities. This replaces the traditional appCache that has caused many management issues since it was created. Service worker caching is much more manageable.
The cache API provides a persistence layer that stores network responses which can be queried by the matching request.
The key to using service worker caching is the service worker 'fetch' event. This event triggers for each network request allowing you to intercept the request and check if the response has been cached before it goes to the network.
By accessing assets in local cache you can forgo expensive network requests. This means the site can work when there is no network, offline, or poor network connectivity, low bars or false cellular connectivity (known as LiFi).
self.addEventListener("fetch", event => {
event.respondWith( fetchFromCache(event)
.catch(() => fetch(request)
.then(response => addToCache(cacheKey, request, response))
.catch(() => offlineResponse(resourceType, opts)) ) );
});
In the example above the code checks is a response has been previously cached. If so, the cached response is returned. If not the request is passed along to the network.
When the request returns the response is cached and returned to the UI.
If the network fails the logic falls back to an offline response.
There are a lot of moving parts being used in the example code. I will provide more details in a follow up articles.
How do I Update My Service Worker Cache
A common misconception is once you cache a network request it is stored for eternity. This is not the case. You have complete control over when and how cache is invalidated and updated.
This is a very complex and involved topic. I think I can personally detail about 3 dozen caching strategies, all involve a way to manage cache invalidation or updating the cache.
I think the key to invalidation is determining either a time to invalidate (time to live) value, a manifest or a background update process. In the later you can make a HEAD request to see if an update has been made on the server by comparing the last-updated header value against the time the response was cached.
There is no single answer for every application. You will need to plan your strategy and be prepared to adjust as you see how your application is used.
What Browsers Support Service Workers?
All modern browsers support service workers, at least caching. Chrome, FireFox and Edge support native push notifications and Chrome and Edge have background sync support.
This means the core PWA features are available in all devices and browsers. Push and background sync are enhancements.
We have built several Progressive Web Apps that required sophisticated offline caching strategies to allow applications work offline, even on iOS. These applications require all requests, even POST, PUT and DELETE actions to be cached in a queue. We made these application sync even on iOS when connections were known to be available.
What is the Difference Between a Service Worker and a Web Worker
Service workers and web workers are similar tools. Both execute processes in a separate thread from the user interface. The real difference lies in the actual usage context.
Neither have access to the DOM or window object. They are better for middle tier or long, process intensive tasks.
Web workers only run when a web page is open and a task is triggered from a script in the page.
A service worker can also execute when a page is open, but may also be triggered by a platform event like a push notification. A web page does not have to be open for a service worker to execute.
Service workers also act as a proxy between the network and the user interface. If a page is being used (the most common scenario) all HTTPS network requests pass through the service worker as you learned in the caching section.
Communication between the UI and both workers is done using the postMessage method and the message event. If you have done any multi-threaded programming this model is very familiar.
What a Service Worker Can't Do
Service Workers Can’t Access the Window Object
The window object executes in the UI thread, separate from the service worker thread. This means a service worker can’t directly manipulate DOM elements. The service worker and the window can communicate via the postMessage method. This allows messages to be passed back and forth. You will need to have logic on each side to process the the messages and trigger different workflows.
Service Workers Require HTTPS
Because Service Workers have so much power they are only enabled if the page is served using HTTPS. This ensures a level of security to allow the service worker to do the things it is designed to do.
Over HTTP the service worker would be susceptible to man in the middle attacks. Developers can work on localhost, which keeps us from installing a local TLS certificate.
In other words the answer to does a service worker work on HTTP is no. The site will still render, but the service worker does not register and is not executed.
Are Only Asynchronous
Service workers are asynchronous, which means they use and rely on Promises and APIs that use Promises. This means synchronous APIs like XHR and localStorage are not available to a service worker. Never fear, you can use the Fetch API (which replaced XHR) and IndexedDB. Most APIs that do not directly interact with the window object are accessible in a service worker.