Service workers in Samsung Internet browser
I was getting reports of some odd behaviour with the service worker on thesession.org, the Irish music website I run. Someone emailed me to say that they kept getting the offline page, even when their internet connection was perfectly fine and the site was up and running.
They didn’t mind answering my pestering follow-on questions to isolate the problem. They told me that they were using the Samsung Internet browser on Android. After a little searching, I found this message on a Github thread about using waitUntil
. It’s from someone who works on the Samsung Internet team:
Sadly, the asynchronos waitUntil() is not implemented yet in our browser. Yes, we will implement it but our release cycle is so far. So, for a long time, we might not resolve the issue.
A-ha! That explains the problem. See, here’s the pattern I was using:
- When someone requests a file,
- fetch that file from the network,
- create a copy of the file and cache it,
- return the contents.
Step 1 is the event listener:
// 1. When someone requests a file
addEventListener('fetch', fetchEvent => {
let request = fetchEvent.request;
fetchEvent.respondWith(
Steps 2, 3, and 4 are inside that respondWith
:
// 2. fetch that file from the network
fetch(request)
.then( responseFromFetch => {
// 3. create a copy of the file and cache it
let copy = responseFromFetch.clone();
caches.open(cacheName)
.then( cache => {
cache.put(request, copy);
})
// 4. return the contents.
return responseFromFetch;
})
Step 4 might well complete while step 3 is still running (remember, everything in a service worker script is asynchronous so even though I’ve written out the steps sequentially, you never know what order the steps will finish in). That’s why I’m wrapping that third step inside fetchEvent.waitUntil
:
// 2. fetch that file from the network
fetch(request)
.then( responseFromFetch => {
// 3. create a copy of the file and cache it
let copy = responseFromFetch.clone();
fetchEvent.waitUntil(
caches.open(cacheName)
.then( cache => {
cache.put(request, copy);
})
);
// 4. return the contents.
return responseFromFetch;
})
If a browser (like Samsung Internet) doesn’t understand the bit where I say fetchEvent.waitUntil
, then it will throw an error and execute the catch
clause. That’s where I have my fifth and final step: “try looking in the cache instead, but if that fails, show the offline page”:
.catch( fetchError => {
console.log(fetchError);
return caches.match(request)
.then( responseFromCache => {
return responseFromCache || caches.match('/offline');
});
})
Normally in this kind of situation, I’d use feature detection to check whether a browser understands a particular API method. But it’s a bit tricky to test for support for asynchronous waitUntil
. That’s okay. I can use a try
/catch
statement instead. Here’s what my revised code looks like:
fetch(request)
.then( responseFromFetch => {
let copy = responseFromFetch.clone();
try {
fetchEvent.waitUntil(
caches.open(cacheName)
.then( cache => {
cache.put(request, copy);
})
);
} catch (error) {
console.log(error);
}
return responseFromFetch;
})
Now I’ve managed to localise the error. If a browser doesn’t understand the bit where I say fetchEvent.waitUntil
, it will execute the code in the catch
clause, and then carry on as usual. (I realise it’s a bit confusing that there are two different kinds of catch
clauses going on here: on the outside there’s a .then()
/.catch()
combination; inside is a try{}
/catch{}
combination.)
At some point, when support for async waitUntil
statements is universal, this precautionary measure won’t be needed, but for now wrapping them inside try
doesn’t do any harm.
There are a few places in chapter five of Going Offline—the chapter about service worker strategies—where I show examples using async waitUntil
. There’s nothing wrong with the code in those examples, but if you want to play it safe (especially while Samsung Internet doesn’t support async waitUntil
), feel free to wrap those examples in try
/catch
statements. But I’m not going to make those changes part of the errata for the book. In this case, the issue isn’t with the code itself, but with browser support.