Skip to content

Instantly share code, notes, and snippets.

@wanderview
Last active October 14, 2015 19:09
Show Gist options
  • Save wanderview/4f0f1bbf8d874842a920 to your computer and use it in GitHub Desktop.
Save wanderview/4f0f1bbf8d874842a920 to your computer and use it in GitHub Desktop.
PoC to extend service worker lifetime past end of fetch event by (ab)using postMessage()
<html>
<head></head>
<body>
<script>
navigator.serviceWorker.addEventListener('message', function(evt) {
if (evt.data === 'CALL ME') {
navigator.serviceWorker.controller.postMessage('RING RING');
}
});
navigator.serviceWorker.register('sw.js');
</script>
</body>
</html>
function doSlowWorker() {
// do something that takes a long time and return a promise
// that resolves when its done
return caches.open('big').then(function(cache) { cache.addAll(hugeList) });
}
self.addEventListener('message', function(evt) {
if (evt.data === 'RING RING') {
evt.waitUntil(doSlowWork());
}
})
self.addEventListener('fetch', function(evt) {
evt.respondWith(Promise.all([
fetch(evt.request),
self.clients.matchAll()
]).then(function(results) {
var response = results[0];
var clientList = results[1];
// We would do our slow work here, but we don't want to block the fetch
// evnt response and we can't safely do it past the end of .respondWith().
// Instead, ask the document to postMessage() us so we can do the slow
// work in a separate .waitUntil(). This will not block the fetch
// event processing.
if (clientList.length > 0) {
clientList[0].postMessage('CALL ME');
}
return response;
}));
})
@wanderview
Copy link
Author

So, it turns out neither Chrome nor Firefox implement .waitUntil() on the message event yet. So this won't work.

Also, the spec now include .waitUntil() on the FetchEvent directly. So this is preferred:

self.addEventListener('fetch', function(evt) {
  evt.respondWith(fetch(evt.request).then(function(response) {
    evt.waitUntil(doSlowWork);
    return response;
  }));
}))

Of course, both chrome and firefox still need to implement FetchEvent.waitUntil().

@wanderview
Copy link
Author

Further correction, you cannot call evt.waitUntil() after the original event handler has returned. So you have to do something like:

self.addEventListener('fetch', function(evt) {
  evt.waitUntil(new Promise(function(resolveWaitUntil) {
    evt.respondWith(fetch(evt.request).then(function(response) {
      resolveWaitUntil(doSlowWork());
      return response;
    }));
  }));
}));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment