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."
});
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');
})
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);
})
/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 => {
// Check if the image is a jpeg
// Inspect the accept header for WebP support
// If we support WebP
// Clone the request
// Build the return URL
});
deanhume.com/service-workers-dynamic-responsive-images-using-webp-images
.addEventListener('fetch', function(event) {
// Clone the request
var req = event.request.clone();
// Check if the image is a jpeg
if (/\.jpg$|.png$/.test(event.request.url)) {
// Get all of the headers
let headers = Array.from(req.headers.entries());
// Inspect the accept header for WebP support
var acceptHeader = headers.find(item => item[0] == 'accept');
var supportsWebp = acceptHeader[1].includes('webp');
// If we support WebP
if (supportsWebp) {
// Build the return URL
var returnUrl = req.url.substr(0, req.url.lastIndexOf(".")) + ".webp";
event.respondWith(
fetch(returnUrl, {
mode: 'no-cors'
})
);
}
}
});
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
addEventListener('install', event => {
event.waitUntil(
addEventListener('install', installEvent => {
skipWaiting();
installEvent.waitUntil(
caches.open(cacheName)
.then(cache => {
return cache.addAll([
'offline.html',
'styles.css'
]);
})
);
});
addEventListener('activate', function (event) {
activateEvent.waitUntil(
caches.keys()
.then(cacheNames => {
return Promise.all(
cacheNames.map( cacheNameKey => {
if (cacheNameKey != cacheName) {
return caches.delete(cacheNameKey);
}
})
);
})
.then( () => {
clients.claim()
})
);
});
Alew Russell & Frances Berriman
infrequently.org/2015/06/progressive-apps-escaping-tabs-without-losing-our-soul/
Aaron Manire
Feedback
nerd.ngo/feedback