Standardized Web API
Run JS in the background
Work offline
Receive push notifications
And much more...
2014: The web is doomed!
2016: Not dead yet..
2016: Apps are starting to saturate
What if we could have the best of both worlds?
Progressive Web Apps
PWA
PoWA?
P - W - A
Workers execute JS on separate threads
See Nolan Lawson's (@nolanlawson) blog and demo about moving storage work to a separate thread.
Things to note about dedicated Workers:
new Worker(scriptURL)
Worker
object is garbage collected
Service Workers are Different
Service Worker Lifecycle
What happens during an update?
Registering a Service Worker
// In your index.html
if (navigator.serviceWorker) {
navigator.serviceWorker.register('sw.js').then(swr => {
console.log('registered service worker!');
}).catch(e => {
console.log('failed to register service worker!');
});
}
Lifecycle Events
// In your service worker script
self.addEventListener('install', evt => {
console.log('Installing!');
});
self.addEventListener('activate', evt => {
console.log('Activating!');
});
Worker thread can be killed while doing async operations
waitUntil()
to the rescue!
// In your service worker script
function asyncWork() {
return new Promise(resolve => { setTimeout(resolve, 5000); });
}
self.addEventListener('install', evt => {
console.log('Install starting');
evt.waitUntil(asyncWork().then(_ => {
console.log('Install complete!');
});
});
self.addEventListener('activate', evt => {
console.log('Activate starting!');
evt.waitUntil(asyncWork().then(_ => {
console.log('Activate complete!');
});
});
Fetch API
fetch('http://example.com/my/rest/api').then(response => {
return response.text();
}).then(text => {
console.log(text);
}).catch(e => {
console.error(e);
});
See MDN for more documentation.
Cache API
var url = 'http://example.com/my/asset';
var cache;
caches.open('my-cache').then(c => {
cache = c;
return fetch(url);
}).then(response => {
return cache.put(url, response);
}).then(_ => {
cache.match(url);
}).then(response => {
return response.text();
}).then(text => {
return cache.delete(url);
}).then(result => {
// result should be true
return caches.delete('my-cache');
}).then(result => {
// result should be true
});
Pre-cache Assets in the Install Event
var cacheName = 'myCache';
var assets = [
'http://example.com/my/asset/1',
'http://example.com/my/asset/2',
];
self.addEventListener('install', evt => {
evt.waitUntil(caches.open(cacheName).then(cache => {
var requests = assets.map(url => new Request(url, { cache: 'no-cache' }));
cache.addAll(requests);
});
});
Cleanup old Assets in the Activate Event
var cacheName = 'myCache';
self.addEventListener('activate', evt => {
evt.waitUntil(caches.keys().then(list => {
return Promise.all(
list.map(name => {
if (name === cacheName) {
return;
}
return caches.delete(name);
});
);
});
});
Fetch Event
self.addEventListener('fetch', evt => {
console.log('Network request for ' + evt.request.url);
});
FetchEvent.request
holds the original request
FetchEvent.respondWith()
allows you to provide a custom response
FetchEvent.respondWith()
also acts like waitUntil()
Which Network Request Get a Fetch Event?
navigator.serviceWorker.controller
Offline Pre-Cached Assets
self.addEventListener('fetch', evt => {
evt.respondWith(caches.open(myCache).then(cache => {
// First try to find a pre-cached response
return cache.match(evt.request);
}).then(response => {
// Fallback to network
return response || fetch(evt.request);
}).catch(e => {
// Fallback to network if we hit an unexpected error
return fetch(evt.request);
}));
});
This slide deck uses a service worker.
What about push notifications?
Moar Resources!