You can’t read any technology trend news for the upcoming year without reading about Progressive Web Applications (PWA). PWA offers the ability to add native-like capabilities to your web application.
One of the core technologies of a PWA is the service worker, so I thought I would take us through a question and answer session on service workers. Service workers have been around for a while now, but the momentum in their use has picked up with the popularity of PWA. My goal here is for you to be able to understand what a service worker is, what you can do with it, and the limitations of a service worker.
Firstly, what is a worker?
So…front end multithreading?
I’ve kind of heard of that, aren’t workers often used for heavy processing?
Yes. A common type of worker is the web worker from the Web Worker API introduced in HTML5. Even if you haven’t used a web worker as a developer, you likely have benefited from a web worker just by being on a website. Remember in the old days (or yesterday) when you opened a web page and it froze? There’s a good chance that it was because it was waiting on some resource-intensive script to finish processing.
A web worker is a solution to the waiting problem. It’s a script that is running independently in the background, regardless of what you might be interacting with on the page and regardless of any other scripts that are running at the same time. A web worker is generic. The only defining characteristic is that it’s running in a separate thread from the browser. Workers are used to remove heavy lifting processes from the main browser thread. They can be used with any process and have a limited lifecycle. They only live as long as the tab is open and a new web worker will be created for every new tab opened. The main thread can create an unlimited number of web workers.
So, what does this have to do with a service worker?
A service worker is a special type of worker created to function as a proxy between the browser and an information supplier in the form of the network or a cache. For this proxy, think in terms of the development proxy pattern: https://en.wikipedia.org/wiki/Proxy_pattern. The service worker runs in an independent thread as all workers do, but service workers are less about heaving lifting and more about proxying information.
The lifecycle is also significantly different from a standard web worker. The service worker exists specifically to function as an interface to the network or cache. Currently, you get one service worker per service worker scope in contrast to the many web workers a single browser thread can create. The single web worker may be shared by many tabs.
What is a service worker, specifically?
What are the main use cases?
One popular set of use-cases includes serving cached content, ranging anywhere from caching only static content to full offline usage, possibly with queued data from the user being pushed back to the server once the network is available again. Another popular use case is pushing, which can range across varying levels of notification and payload interactions. Basically, anything that needs to outlive the document can be a use case. A great resource for recipes can be found in the Service Worker Cookbook.
I hear PWA and SPA mentioned together a lot. Does my app have to be a SPA to use service workers?
No. SPA (Single Page Applications) can utilize service workers, but a Single Page Application is not required to benefit from the background processes that power Progressive Web Applications (PWA).
Does the use of a service worker make my app PWA?
Well, service workers are an important part of making a web application behave more like a native application, but there are some other requirements to be considered PWA, too, such as responsive design.
You said the lifecycle is different than a standard web worker. How so?
As we discussed above, a standard web worker is tied to the browser tab that started the worker. A service worker can be tied to many browser tabs and one event may affect all associated tabs.
The lifecycle of service workers consists of three phases: registration/download, installation, and activation. While these do function as you would expect by the names, they are also fairly nuanced, especially installation. A good place to start learning the service worker lifecycle is: https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle
Okay, after my service worker is activated then what happens?
Like any daemon type thread, your service worker will keep on living no matter if the browser is closed or the app is otherwise terminated. It helps to think of service worker lives being tied to specific events instead of documents.
The browser now controls the service worker and may decide to shut it down to protect resources. If that happens it should be reactivated on the next relevant http request.
Wait, how do service workers get removed if they are independent of the browser thread?
Service workers can be removed programmatically by using an unregister code where you would normally register the worker. They can also be removed manually via the browser.
How do I debug my new code?
Chrome offers a lot of great tools for developers working with service workers. A good breakdown of the basic open developer tools for major browsers can be found here: https://developers.google.com/web/ilt/pwa/tools-for-pwa-developers.
Keep an eye on your console log statements, because they may be removed before a page reloads and you might not see them without changing some settings. There may be some other gotchas, for example, if you are wanting to test caching functionality you won’t want to have cache disabled in your developer console.
You mentioned scopes earlier. Can you tell me more?
As discussed above, currently only one service worker can exist per scope. The scope is dependent on the URL path. The default scope of the service worker depends on where the worker is located, including the current directory and subdirectories, and can be limited further when set during registration. Remember, this code is acting as a proxy, so the scope, in this case, is the path from which the worker can intercept network calls. It’s important to note that a standard service worker setup can only intercept requests from its own origin.
What about events? What kinds of events do you mean?
The first set of events is tied to the setup of the service worker (think
activate). After that, events include network or cache
fetch events, notification events like
push, or background sync events like
What browsers support service workers?
All major browsers offer support for service workers, including Edge and Safari. This site has a great breakdown of service worker related support by browser and feature: https://jakearchibald.github.io/isserviceworkerready/. If it’s likely your users are on older versions code accordingly, of course.
Do I need this in my app?
It’s new and shiny. Well, like most things in software development…maybe. Let’s go over some scenarios.
I’m already using HTML5 Application Cache, do I need to start using a service worker instead?
Yes, you will want to update soon. If you are already using the HTML5 Application Cache, and plan to keep using cache, you absolutely will need to learn service workers because the feature is being removed from web standards and likely will lose browser support very soon, if it hasn’t already.
While many agree that application cache is often fraught with issues, and you may not be sad to see it go, there is some debate over whether the recommended service worker implementation is overkill. As with most things, it probably depends on the use. My guess would be the parts people have issues with, like code duplication, will eventually be resolved.
I have a lot of static content that never changes, would a service worker be helpful here?
Yes, this is a good basic cache use case for a service worker. You can add the static content during installation, and it will always be readily available to the user.
My users are often offline traveling or, heck, out in the wilderness fishing. Or maybe they just frequently have a flaky network. Or they’re one of the people who still don’t have internet access at home. Do I need this?
Yes, an application known to be used where connectivity is poor or nonexistent would be a prime candidate. Do your users travel frequently where they are forced to be offline? Are they in disconnected locations/populations? Do you want your website to function more like a mobile app when offline?
You may benefit from offering a fallback option for offline users and embedded fallback is one of the standard service worker recipes.
This is also a good time to discuss background sync, another popular use of service workers. It’s happened to all of us, you are about to submit a form when…boom, your phone is bumped off the data network (why?!?). In the past, the user would have had to go back and repeat the steps themselves. With background sync, the request can be queued (usually by way of IndexedDB) and sent when the connection is back to normal.
I want a way to engage my users when they are offline or have left my application, would this help?
Yes. Service workers can provide a great option for notification and push features, even when the application is not running. These can come from the local app or the server.
Keep in mind that the user needs to accept receiving notifications before you start sending them. Even if the browser doesn’t force acceptance, yet, it will soon. You need to account for the possibility of a user declining notifications in your code. Obviously, if you want people to keep using your app, you’ll want to be judicious in using these. Think timely and relevant information only.
Once the user has agreed, you can now communicate content even if the user doesn’t have the website up. If you decide to use push notifications, you will need to use the Push API and the Notification API. Push is how the service worker will be told to send a notification and the notification will describe how the information is shown to the user. If this seems confusing, remember that pushes can also update the state of the application and may have nothing to do with notifying a user.
Per the Push API documentation, currently, browsers handle notifications differently, so be aware that there is no current standard mechanism for handling notifications and some browsers put quotas on what you can send.
Once you push a notification, you can use the service worker to focus back on your tab or even reopen it.
I don’t need any kind of notifications, my users are well connected, and most of my content can’t be easily cached, but I hear this is the all the rage, so should I?
Maybe not. If you are primarily benefiting from the offline gains make sure that your users really would be offline. Many of us are fortunate enough to be very well connected in 2019. That said, this technology is likely to pick up momentum so learning it is still a great idea, even if you don’t apply it yet.
What else do I need to keep in mind when using a service worker?
Glad you asked! Here is a breakdown of some more things you will want to know:
DOM Manipulation – Running in a separate thread, all workers have limitations and service workers are no different. They cannot directly access the DOM. Any interactions with the DOM from a service worker will need to be communicated to the main thread through some implementation of message relay.
No Window or Parent Abjects Access – Another general limitation of workers.
Website Level Access to Devices – While you may be able to make your web application behave in a more like a native app in terms of offline access and notifications, you still have the limited access to devices of a web application. This access varies by device, seems to be constantly changing, and can touch a whole host of privacy issues. Just be aware you may hit limitations.
You Can’t Rely on State – Since the service worker can be terminated and restarted at any time by the browser, nothing can be persisted/counted on to be available. Which brings us to…
Local Storage (and other Synchronous APIs) – You cannot use a service worker with local storage or other synchronous APIs. This makes sense when you remember that the service worker is completely asynchronous. If you are considering local storage, you will want to consider IndexedDB instead, since it is asynchronous.
Security – Service workers absolutely must run on HTTPS because of a far greater risk of man in the middle attacks. You absolutely must verify who is listening and communicating. The good news is
localhost is considered secure so playing around with the technology locally to start and working your development is covered.
Complexity – Any time you implement multiple threads, completely asynchronous tasks, and caching you have added complexity from a development standpoint. And to emphasize that point more…
Caching – The developer of the service worker must manage when the cache is updated. Different requests require different caching strategies and invalidation strategies. These are notoriously complex operations from a development standpoint. Anyone who has been forced to track down a bug in caching code probably knows it can become…fun. The user may gain an application that feels speedy and offline access, but the application may also soon hit serious data integrity bumps if you aren’t careful in the design.
There are many great books on this topic you’ll want to start reading now. Also, make sure you benefit from caching before you start down this path! Don’t try to hide a slow backend API or other non-network related problem with front end caching, it won’t end well. This will have the added benefit of reducing the risk the next person who works on your app hunting you down for nefarious reasons.
You Don’t Control What Happens to the Service Worker – A savvy user can just deactivate all service workers in Chrome, for example, so you always have to code for the case of the user not having the service worker. And while we are on this topic, workers will need to be periodically updated on the client-side, which there’s always the possibility of something not going the way you think it will. I’m just going to call this one now, I’ve seen no proof yet, but this seems to be a general law of life in software development.
Now that we’ve covered all that I hope you feel more confident in your understanding of service workers. I’m excited to see what happens as this technology grows and changes in the coming years. Hopefully, now you are too!
Ready to keep going? Here is some continued good reading to get you started.