Service Worker
Reliability bits
Jungkee Song
Senior engineer at Samsung
Web standards author
Chromium contributor
Email: jungkee.song@samsung.com
GitHub: @jungkees
Samsung Internet
4.0 shipped SW!
Service Worker
Reliability
Progressive Web Apps
Progressive Web Apps
• Reliability
− Fast loading, works on flaky networks
• First-class
− Do what native does, background
• Engaging
− Home screen access, push notification
Progressive Web Apps
Service Worker
What to solve
Problems
Problems
Not only
But ..
Problems
• Background service
− Was not possible without a page
The Total Game Changer!
https://youtu.be/QiXLaisCq10
Udacity Offline-First Web Applications Trailer
Service Worker
Concepts
What to solve
Your programable proxy
Page
Browser
Service Worker
Cache
NetloaderandHTTPcache
Service Worker
Concept
Script runs in the background
A type of web worker
var worker = new Worker(‘dedicated_worker.js');
var worker = new SharedWorker(‘shared_worker.js’);
navigator.serviceWorker.register(“/service_worker.js");
Service Worker
Event-driven
Responds to events
fetch event
Navigation/Resource request
onfetch
Page
SW
Cache
Attempt to match cache
Matched Response
Respond to client
Page Page
Event-based worker
Service Worker
Power matters
Intentionally short lifetime
Bound to page
Page
Worker
new Worker(‘job.js’)
Run job.js
t
Dedicated worker
* Lifetime is bound to the page unless .terminate is called.
Event-driven
Page
SW
fetch event
e.g.<img src=‘pic.png’>
Run sw.js
Browser internal
t
Service worker
Term
inate
Start
onfetch
e.waitUntil
push eventStart
Term
inate
onpush
e.waitUntil
Registration and SW
registration active worker waiting worker installing worker
ServiceWorkerRegistration
active
active
installing
Persistent
Client scope
fetch install
registration.active
Browser
Internals
Script
Surface
registration.active
registration.installing
ServiceWorkerRegistration
ServiceWorker
ServiceWorker
ServiceWorker
Page Page
Service Worker
Make it secure
HTTPS
HTTPS
• SW allows powerful stuffs
− Intercept requests and fabricate your
own responses
• Need to guarantee it has not been tempered
during its own fetch
− localhost is allowed during development
− HTTPS setup is needed to deploy
Service Worker
Get it ready
Install and Update
and every navigation and functional event
Install
var navigator.serviceWorker;
sw.register(scriptURL, { scope: scopeURL });
Pre-cache resources
oninstall = e => { /* pre-cache here */ };
Handle fetch event
onfetch = e => { /* respond with magic */ };
Manage Cache
onactivate = e => { /* Deleting cache is on you */ };
Update
registration.update();
“/assets/v1” /assets/v1/serviceworker.js
[ Registration map ]
Scope Script URL
Register
From page
// scope defaults to "/"
var sw = navigator.serviceWorker;
sw.register("/assets/v1/serviceworker.js")
.then(reg => {
console.log("success!");
reg.installing.postMessage("Howdy from your installing page.");
})
.catch(e => {
console.error("Installing the worker failed!:", e);
});
“/bar” /bar/sw1.js
[ Registration map ]
Scope Script URL
“/foo” /foo/sw.js
Register – Multiple registrations
From page
var sw = navigator.serviceWorker;
① sw.register('/bar/sw1.js', { scope: '/bar' });
② sw.register('/foo/sw.js', { scope: '/foo' });
③ sw.register('/bar/sw2.js', { scope: '/bar' } );
“/bar” /bar/sw2.js
onactivate
SW
oninstall
onfetch
Browser internals
oninstall = e => {
// pre-cache, etc.
};
onactivate = e => {
// upgrade Cache, etc.
};
onfetch = e => {
// Not yet ready
};
Fetched sw script
Fetch sw script
Update① ③ Install
④ Activate
onfetch
onfetch = e => {
// Not yet ready
};
Install process triggered by register
Evaluate②
installinginstalledactivated
Service Worker
How interception work
Client matching
&
Subresource request
onfetch
sw.js
Cache
Attempt to match cache
Matched response
Respond to client
“/” /sw.js
[ Registration map ]
Scope Script URL
“/foo” /foo/sw.js
Page
Navigate https://example.com/index.html
fetch event
Scope matching
Run SW
Client matching (navigation)
onfetch
sw.js
Cache
Attempt to match cache
Matched response
Respond to client
“/” /sw.js
[ Registration map ]
Scope Script URL
“/img” /img/sw.js
Page
Fetch “https://example.com/img/flower.png
fetch event
Control
Run SW
Subresource request
Service Worker
How to handle fetch
Patterns
Cache of static resources in oninstall
https://jakearchibald.com/2014/offline-cookbook/#on-install-as-a-dependency
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open('mysite-static-v3').then(function(cache) {
return cache.addAll([
'/css/whatever-v3.css',
'/css/imgs/sprites-v6.png',
'/css/fonts/whatever-v8.woff',
'/js/all-min-v4.js'
// etc
]);
})
);
});
Cache on user demand
document.querySelector('.cache-article').addEventListener('click', function(event) {
event.preventDefault();
var id = this.dataset.articleId;
caches.open('mysite-article-' + id).then(function(cache) {
fetch('/get-article-urls?id=' + id).then(function(response) {
// /get-article-urls returns a JSON-encoded array of
// resource URLs that a given article depends on
return response.json();
}).then(function(urls) {
cache.addAll(urls);
});
});
});
https://jakearchibald.com/2014/offline-cookbook/#on-user-interaction
Cache of dynamic resource
https://jakearchibald.com/2014/offline-cookbook/#on-network-response
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open('mysite-dynamic').then(function(cache) {
return cache.match(event.request).then(function (response) {
return response || fetch(event.request).then(function(response) {
cache.put(event.request, response.clone());
return response;
});
});
})
);
});
Cache migration in onactivate
https://jakearchibald.com/2014/offline-cookbook/#on-activate
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.filter(function(cacheName) {
// Return true if you want to remove this cache,
// but remember that caches are shared across
// the whole origin
}).map(function(cacheName) {
return caches.delete(cacheName);
})
);
})
);
});
onpush
https://jakearchibald.com/2014/offline-cookbook/#on-push-message
self.addEventListener('push', function(event) {
if (event.data.text() == 'new-email') {
event.waitUntil(
caches.open('mysite-dynamic').then(function(cache) {
return fetch('/inbox.json').then(function(response) {
cache.put('/inbox.json', response.clone());
return response.json();
});
}).then(function(emails) {
registration.showNotification("New email", {
body: "From " + emails[0].from.name
tag: "new-email"
});
})
);
}
});
self.addEventListener('notificationclick', function(event) {
if (event.notification.tag == 'new-email') {
// Assume that all of the resources needed to render
// /inbox/ have previously been cached, e.g. as part
// of the install handler.
clients.openWindow(‘/inbox/');
}
});
Offline Cookbook
https://jakearchibald.com/2014/offline-cookbook/
Application Shell
• Minimal HTML, CSS, and JavaScript powering UI
• Should
− Load fast
− Be cached
− Dynamically display content
https://developers.google.com/web/updates/2015/11/app-shell
Service Worker
Yeah, it works!
Demo
http://stories.flipkart.com/introducing-flipkart-lite/
Service Worker
Yeah, we want more!
Status
Standards
Implementation
4th WD in June 2105
Plan a CR in the 3Q of this year
4.0 40+ 44 32+
Service Worker
And.. Your turn!
In the wild
https://pwa.rocks/
Service Worker
V2 stuffs
Future
V2 features
• Foreign fetch
• Cache transaction
• Declarative register

Service Worker - Reliability bits