Service Workers
Bring your own magic
Jungkee Song
Github: @jungkees
Twitter: @jungkees
Google+: +JungkeeSong
See this slide with animation here!
Service Workers solve ..
● Offline usage
○ Offline-first
○ Sorry, no magic. Create your own!
■ Programmable cache control
■ Custom response - Constructor, IDB, etc.
● Background processing
○ Wanna do things while UA’s not running?
○ Push messages, Alarms (Task Scheduler),
BackgroundSync, etc.
Installed!
Work in progress
Activating!
https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html
https://github.com/slightlyoff/ServiceWorker
Work in progress
● Lifecycle events
Principles and Terms
● Runs on same origin
● Registration keyed by URL scope
● Document is controlled by matching SW
upon navigation
● Successfully installed worker is considered
worker in waiting
● Functional events
Are you online?
Navigation/Resource request
Page
Network fetch
Response
Are you sufficiently online?
Navigation/Resource request
Page
Network fetch
4XX
5XX
Timeout
DNS failure
fetch event
Have a Service Worker?
Navigation/Resource request
onfetch
Page
SW
Cache
self.caches.match(url)
Promise<response>
e.respondWith
(Promise<response>)
IDB
new Response({
status: 200,
body: { … }
})
Offline-first
fetch event
Now fallback to network with SW
Navigation/Resource request
onfetch
Page
SW
Cache
self.fetch(request)
self.caches.match(url)
Promise rejects
e.respondWith
(Promise<response>)
Offline-first
fetch event
Event-driven worker
Navigation/Resource request
onfetch
Page
SW
Cache
self.caches.match(url)
Promise<response>
e.respondWith
(Promise<response>)
Page Page
Navigation/Resource request
fetch event
e.respondWith
(Promise<response>)
Key concept
// scope defaults to "/*"
navigator.serviceWorker.register("/assets/v1/serviceworker.js").then(
function(serviceWorker) {
console.log("success!");
serviceWorker.postMessage("Howdy from your installing page.");
// To use the serviceWorker immediately, you might call
// window.location.reload()
}, function(why) {
console.error("Installing the worker failed!", why);
});
Registration
● In the page
“/*” /assets/v1/serviceworker.js
[ Registration map ]
Scope Script URL
Service Worker Lifecycle
Registration
● In the page
navigator.serviceWorker.register("/sw.js");
“/*” /sw.js
[ Registration map ]
Scope Script URL
“/foo/*” /foo/sw.js
“/*” /bar/sw.js
Service Worker Lifecycle
navigator.serviceWorker.register("/foo/sw.js", { scope: “/foo/*” });
navigator.serviceWorker.register("/bar/sw.js");
Installation
● Registration triggers installation of the SW
● UA fires install event to the installing
Service Worker
● The event handler may extend the lifetime
of SW for preparing its caches
Service Worker Lifecycle
Installation: oninstall
● In the Service Worker context
// caching.js
this.addEventListener("install", function(e) {
// Create a cache of resources. Begins the process of fetching them.
var shellResources = new Cache();
// The coast is only clear when all the resources are ready.
e.waitUntil(shellResources.add(
"/app.html",
"/assets/v1/base.css",
"/assets/v1/app.js",
"/assets/v1/logo.png",
"/assets/v1/intro_video.webm",
));
// Add Cache to the global so it can be used later during onfetch
self.caches.set("shell-v1", shellResources);
});
Service Worker Lifecycle
Programmable cache control
● new Cache()
[Constructor]
interface Cache {
Promise<AbstractResponse>
match((Request or ScalarValueString) request, optional QueryParams params);
Promise<sequence<AbstractResponse>>
matchAll((Request or ScalarValueString) request, optional QueryParams params);
Promise<any> add((Request or ScalarValueString)... requests);
Promise<any> put((Request or ScalarValueString) request, AbstractResponse response);
Promise<any>
delete((Request or ScalarValueString) request, optional QueryParams params);
Promise<any> each(CacheIterationCallback callback, optional object thisArg);
};
Service Worker Lifecycle
● Worker in waiting
○ Once self.oninstall() ends
○ So to speak, the installation successfully done
○ This is not yet controlling the documents in scope
● navigator.serviceWorker.controller
○ When all the active documents in scope unload
○ The worker in waiting becomes active worker
○ self.clients.reloadAll() works
○ event.replace() works
Have a controller yet?
Service Worker Lifecycle
● In the Service Worker context
this.addEventListener("fetch", function(e) {
// No "onfetch" events are dispatched to the ServiceWorker until it
// successfully installs.
// All operations on caches are async, including matching URLs, so we use
// Promises heavily. e.respondWith() even takes Promises to enable this:
e.respondWith(
caches.match(e.request).catch(function() {
return e.default();
}).catch(function() {
return caches.match("/fallback.html");
})
);
});
Handle a fetch: onfetch
Functional event processing
Fetch: navigation request
onfetch
sw.js
Cache
self.caches.match(url)
Promise<response>
e.respondWith
(Promise<response>)
“/*” /sw.js
[ Registration map ]
Scope Script URL
“/foo/*” /foo/sw.js
Page Hit “https://example.com/index.html
fetch event
Scope matching
Run SW
Functional event processing
Fetch: subresource request
onfetch
sw.js
Cache
self.caches.match(url)
Promise<response>
e.respondWith
(Promise<response>)
“/*” /sw.js
[ Registration map ]
Scope Script URL
“/foo/*” /foo/sw.js
Page
Fetch “https://example.com/img/flower.png
fetch event
Control
Run SW
Functional event processing
Updating triggered by
● Registration
● Automatic by UA
● Successful navigation matching
● self.update()
Service Worker Lifecycle
Updating
onfetch
sw-v2
Cache
self.caches.match(url)
Promise<response>
e.respondWith
(Promise<response>)
“/*” /sw-v1
[ Registration map ]
Scope active
fetch event
-
waiting
Page
sw-v1
_Update
_Install
Page
sw-v1
/sw-v2 /sw-v2-
Page
sw-v2
Fetch “https://example.com/img/flower.png
Run SW
Service Worker Lifecycle
Security
● Origin relativity
● Cross origin resource
● HTTPS-only?
○ Protect end users from man-in-the-middle attacks
○ Existing "playground" services (e.g. github.io) now
work with HTTPS
○ HTTPS is coming across much more of the web
quickly
○ Devtools can loosen the restriction for development
● Event-driven workers
○ Free to shutdown the worker when handler’s done
○ “Write your workers as though they will die after
every request”
● Keep the onactivate short
● Platform considerations
○ Enhance matching navigation
○ Events implicitly filter
○ Enhance startup
Performance
Is it ready for you?
● Chrome Canary
○ Partial under flag
○ chrome://flags/#enable-service-worker
● Firefox Nightly
○ Partial under flag
○ about:config > dom.serviceWorkers.enabled
● Stay alerted!
○ Jake’s “Is ServiceWorker ready?”
References and Practices
● Service Worker - first draft published - Jake
Archibald
● Specification
● Github’s explainer
● Github’s implementation considerations

Service workers

  • 1.
  • 2.
    Jungkee Song Github: @jungkees Twitter:@jungkees Google+: +JungkeeSong See this slide with animation here!
  • 3.
    Service Workers solve.. ● Offline usage ○ Offline-first ○ Sorry, no magic. Create your own! ■ Programmable cache control ■ Custom response - Constructor, IDB, etc. ● Background processing ○ Wanna do things while UA’s not running? ○ Push messages, Alarms (Task Scheduler), BackgroundSync, etc.
  • 4.
  • 5.
  • 6.
    ● Lifecycle events Principlesand Terms ● Runs on same origin ● Registration keyed by URL scope ● Document is controlled by matching SW upon navigation ● Successfully installed worker is considered worker in waiting ● Functional events
  • 7.
    Are you online? Navigation/Resourcerequest Page Network fetch Response
  • 8.
    Are you sufficientlyonline? Navigation/Resource request Page Network fetch 4XX 5XX Timeout DNS failure
  • 9.
    fetch event Have aService Worker? Navigation/Resource request onfetch Page SW Cache self.caches.match(url) Promise<response> e.respondWith (Promise<response>) IDB new Response({ status: 200, body: { … } }) Offline-first
  • 10.
    fetch event Now fallbackto network with SW Navigation/Resource request onfetch Page SW Cache self.fetch(request) self.caches.match(url) Promise rejects e.respondWith (Promise<response>) Offline-first
  • 11.
    fetch event Event-driven worker Navigation/Resourcerequest onfetch Page SW Cache self.caches.match(url) Promise<response> e.respondWith (Promise<response>) Page Page Navigation/Resource request fetch event e.respondWith (Promise<response>) Key concept
  • 12.
    // scope defaultsto "/*" navigator.serviceWorker.register("/assets/v1/serviceworker.js").then( function(serviceWorker) { console.log("success!"); serviceWorker.postMessage("Howdy from your installing page."); // To use the serviceWorker immediately, you might call // window.location.reload() }, function(why) { console.error("Installing the worker failed!", why); }); Registration ● In the page “/*” /assets/v1/serviceworker.js [ Registration map ] Scope Script URL Service Worker Lifecycle
  • 13.
    Registration ● In thepage navigator.serviceWorker.register("/sw.js"); “/*” /sw.js [ Registration map ] Scope Script URL “/foo/*” /foo/sw.js “/*” /bar/sw.js Service Worker Lifecycle navigator.serviceWorker.register("/foo/sw.js", { scope: “/foo/*” }); navigator.serviceWorker.register("/bar/sw.js");
  • 14.
    Installation ● Registration triggersinstallation of the SW ● UA fires install event to the installing Service Worker ● The event handler may extend the lifetime of SW for preparing its caches Service Worker Lifecycle
  • 15.
    Installation: oninstall ● Inthe Service Worker context // caching.js this.addEventListener("install", function(e) { // Create a cache of resources. Begins the process of fetching them. var shellResources = new Cache(); // The coast is only clear when all the resources are ready. e.waitUntil(shellResources.add( "/app.html", "/assets/v1/base.css", "/assets/v1/app.js", "/assets/v1/logo.png", "/assets/v1/intro_video.webm", )); // Add Cache to the global so it can be used later during onfetch self.caches.set("shell-v1", shellResources); }); Service Worker Lifecycle
  • 16.
    Programmable cache control ●new Cache() [Constructor] interface Cache { Promise<AbstractResponse> match((Request or ScalarValueString) request, optional QueryParams params); Promise<sequence<AbstractResponse>> matchAll((Request or ScalarValueString) request, optional QueryParams params); Promise<any> add((Request or ScalarValueString)... requests); Promise<any> put((Request or ScalarValueString) request, AbstractResponse response); Promise<any> delete((Request or ScalarValueString) request, optional QueryParams params); Promise<any> each(CacheIterationCallback callback, optional object thisArg); }; Service Worker Lifecycle
  • 17.
    ● Worker inwaiting ○ Once self.oninstall() ends ○ So to speak, the installation successfully done ○ This is not yet controlling the documents in scope ● navigator.serviceWorker.controller ○ When all the active documents in scope unload ○ The worker in waiting becomes active worker ○ self.clients.reloadAll() works ○ event.replace() works Have a controller yet? Service Worker Lifecycle
  • 18.
    ● In theService Worker context this.addEventListener("fetch", function(e) { // No "onfetch" events are dispatched to the ServiceWorker until it // successfully installs. // All operations on caches are async, including matching URLs, so we use // Promises heavily. e.respondWith() even takes Promises to enable this: e.respondWith( caches.match(e.request).catch(function() { return e.default(); }).catch(function() { return caches.match("/fallback.html"); }) ); }); Handle a fetch: onfetch Functional event processing
  • 19.
    Fetch: navigation request onfetch sw.js Cache self.caches.match(url) Promise<response> e.respondWith (Promise<response>) “/*”/sw.js [ Registration map ] Scope Script URL “/foo/*” /foo/sw.js Page Hit “https://example.com/index.html fetch event Scope matching Run SW Functional event processing
  • 20.
    Fetch: subresource request onfetch sw.js Cache self.caches.match(url) Promise<response> e.respondWith (Promise<response>) “/*”/sw.js [ Registration map ] Scope Script URL “/foo/*” /foo/sw.js Page Fetch “https://example.com/img/flower.png fetch event Control Run SW Functional event processing
  • 21.
    Updating triggered by ●Registration ● Automatic by UA ● Successful navigation matching ● self.update() Service Worker Lifecycle
  • 22.
    Updating onfetch sw-v2 Cache self.caches.match(url) Promise<response> e.respondWith (Promise<response>) “/*” /sw-v1 [ Registrationmap ] Scope active fetch event - waiting Page sw-v1 _Update _Install Page sw-v1 /sw-v2 /sw-v2- Page sw-v2 Fetch “https://example.com/img/flower.png Run SW Service Worker Lifecycle
  • 23.
    Security ● Origin relativity ●Cross origin resource ● HTTPS-only? ○ Protect end users from man-in-the-middle attacks ○ Existing "playground" services (e.g. github.io) now work with HTTPS ○ HTTPS is coming across much more of the web quickly ○ Devtools can loosen the restriction for development
  • 24.
    ● Event-driven workers ○Free to shutdown the worker when handler’s done ○ “Write your workers as though they will die after every request” ● Keep the onactivate short ● Platform considerations ○ Enhance matching navigation ○ Events implicitly filter ○ Enhance startup Performance
  • 25.
    Is it readyfor you? ● Chrome Canary ○ Partial under flag ○ chrome://flags/#enable-service-worker ● Firefox Nightly ○ Partial under flag ○ about:config > dom.serviceWorkers.enabled ● Stay alerted! ○ Jake’s “Is ServiceWorker ready?”
  • 26.
    References and Practices ●Service Worker - first draft published - Jake Archibald ● Specification ● Github’s explainer ● Github’s implementation considerations