Aaron Manire
An API for running scripts in the background independently of any user interface scripts
Workers are expected to be long-lived, have a high start-up performance cost, and a high per-instance memory cost
Source: Google/SOASTA Research 2017
addEventListener('fetch', fetchEvent => {
// If fetch is successful, return results.
// …
// If fetch fails, return cached content.
// …
});
A promise can either be fulfilled (resolved) or rejected.
developers.google.com/web/fundamentals/primers/promises
var promise = new Promise((resolve, reject) => {
// Do something, e.g. fetch a file from the network.
if (/* everything turned out fine */) {
resolve("Stuff worked!");
}
else {
reject(Error("It broke"));
}
});
promise
.then(result => {
console.log(result); // "It worked!"
})
.catch(error => {
console.log(error); // Error: "It broke."
});
promise
.then(result => {
console.log(result); // "It worked!"
})
.then(data => {
console.log(data); // "Do something afterwards."
})
.catch(error => {
console.log(error); // Error: "It broke."
});
fetch(request)
.then(responseFromFetch => {
console.log(responseFromFetch); // "It fetched the thing!"
})
.catch(error => {
console.log(error); // Error: "It failed."
});
fetch('https://api.github.com/users/amanire')
.then(response => {
return response.json()
})
.then(data => {
console.log(data);
})
.catch(err => {
console.error(err);
})
Cache instances are not part of the browser’s HTTP cache…
Updates must be manually managed
authors should version their caches by name…w3c.github.io/ServiceWorker/#cache-lifetimes
Caches
property
caches.open(cacheName)
.then(cache => {
// Do something with your cache
});
developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/caches
cache.add()
cache.put()
cache.match()
cache.keys()
cache.delete()
caches.open(cacheName)
.then(cache => {
return cache.add('offline.html');
})
/index.html
via inline script tag
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/serviceworker.js');
}
</script>
No modern JavaScript here!
Service worker file is ignored if unsupported by browser.
<script>
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/serviceworker.js',
{scope: '/app/'}
);
}
</script>
/serviceworker.js
const version = 'V0.1';
const cacheName = 'MySWCache' + version;
addEventListener('install', installEvent => {// …});
addEventListener('activate', activateEvent => {// …});
addEventListener('fetch', fetchEvent => {// …});
addEventListener('install', installEvent => {
// Cache stuff for later
});
addEventListener('activate', activateEvent => {
// Cleanup caches
});
addEventListener('fetch', fetchEvent => {
// Return fetched or cached stuff
});
addEventListener('install', installEvent => {
// Cache stuff for later
});
// cacheName == 'MySWCacheV0.1'
addEventListener('install', installEvent => {
installEvent.waitUntil(
caches.open(cacheName)
.then(cache => {
return cache.addAll([
'offline.html',
'styles.css'
]);
})
);
});
addEventListener('activate', activateEvent => {
// Cleanup caches
});
// Delete old cacheName != 'MySWCacheV0.1'
addEventListener('activate', function (event) {
activateEvent.waitUntil(
caches.keys()
.then(cacheNames => {
return Promise.all(
cacheNames.map( cacheNameKey => {
if (cacheNameKey != cacheName) {
return caches.delete(cacheNameKey);
}
})
);
})
);
});
addEventListener('fetch', fetchEvent => {
// - Fetch HTML file from network
// If error, return offline fallback
//
// - Get all other assets from cache
// If not in cache, fetch from network
});
addEventListener('fetch', fetchEvent => {
// Fetch HTML file from network
const request = fetchEvent.request;
if(request.headers.get('Accept').includes('text/html')) {
fetchEvent.respondWith(
fetch(request)
.then(responseFromFetch => {
return responseFromFetch;
})
// If error, return offline fallback
.catch(error => {
return caches.match('/offline.html');
})
);
return;
}
// …
// Get all other assets from cache
fetchEvent.respondWith(
caches.match(request)
.then(responseFromCache => {
if(responseFromCache) {
return responseFromCache;
}
// If not in cache, fetch from network
return fetch(request);
})
)
});
addEventListener('fetch', fetchEvent => {
// - If the network fails or the response is not served before timeout,
// reject the network request and return the cached version.
//
// - Otherwise, fullfill the promise and
// return fetched file from network
});
serviceworke.rs/strategy-network-or-cache.html
addEventListener('fetch', fetchEvent => {
// Clone the request
// Check if the image is a jpeg
// Inspect the accept header for WebP support
// If we support WebP
// Build the return URL
});
deanhume.com/service-workers-dynamic-responsive-images-using-webp-images
Set console context to Service Worker
Force the new waiting service worker to become the active service worker
Audits for performance, accessibility, progressive web apps, and more.
Alew Russell & Frances Berriman
infrequently.org/2015/06/progressive-apps-escaping-tabs-without-losing-our-soul/
Jeremey Keith
adactio.com/journal/13884
events.drupal.org/vienna2017/sessions/offline-core
Add a progressive web app module to core
drupal.org/project/ideas/issues/2830668
Abandoned, no official release
8.x-1.0 Release, single maintainer
Fully working D8 version based on D7 Serviceworker
drupal.org/project/pwa/issues/3060759
8.x-1.0 Release, incorporates push notifications
Aaron Manire