If you want to make a simple page like the one below work without connection, you have an exciting browser feature - Service Worker.
1 | <head> |
Not every browser supports service workers yet, as we can see from http://caniuse.com/#feat=serviceworkers
We can test if the service worker is supported before loading its code by checking the
navigator
object
1 | <script> |
Inside the service worker script sw.js
we can cache all resources when the worker is executed
for the first time (the install
event). Then we can intercept fetch
events from the page
(which include both Ajax and static requests) and serve the cached versions. If the server
goes down, or WiFi drops, the page continues to work by serving the originally cached resources.
1 | self.addEventListener('install', event => { |
Notice this code does not even include error handling, yet it is a little hard to read.
Mostly because it combines event callback with a promise chain in both cases. For example
the fetch
event waits on the promise we return while searching for the matching cached
response
1 | event.respondWith( |
Imagine if we wanted to fetch a resource if it was not found in the cache, the promise chain would keep growing. The code does not look as bad as a callback pyramid of Doom, yet is still complicated and hard to read.
Well, some people argue that async / await functions are better than promises.
Maybe yes maybe no, but the important thing about async
functions is that they return a Promise!
1 | function async foo() { return 'bar' } |
Great, how does it help us inside the service worker script? If you look at the browser support for async functions, it covers all browsers that support service worker and more.
caution Samsung mobile browser that supports service workers does NOT support async functions
yet, thus the code below will not work. I suggest transpiling the sw.js
and serving the ES5
version based on user agent header.
The wide browser support means we can use the async functions to code our service worker and it should just work. The same code as above looks much cleaner if we separate caching logic into their own functions. The service worker life cycle event handlers become just tiny glue functions.
1 | const cacheResources = async () => { |
Perfecto!