Skip to content

Instantly share code, notes, and snippets.

@NekR
Last active August 20, 2016 09:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NekR/d2d70f4e34d8829ea3c078456351ddce to your computer and use it in GitHub Desktop.
Save NekR/d2d70f4e34d8829ea3c078456351ddce to your computer and use it in GitHub Desktop.
const preloads = [
'/main.js',
'/main.css',
'/logo.png'
];
self.addEventListener('fetch', event => {
const request = event.request;
const url = new URL(request.url);
// Detect if it's a preflight request
if (request.mode === 'navigate' && request.headers.get('Service-Worker-Preflight')) {
let result;
// Note that self.request.match() should take into account
// existence of Service-Worker-Preflight header
// Case A: Construct the page on the fly (FB case)
if (url.pathname === '/newsfeed') {
result = self.requests.match(request).then(res => {
// `res` returns dynamic data, the shell is cached.
// Streaming rendering could be used here too
return constructThePage(res);
});
// Preload assets in case response wasn't returned yet
preloadAssets();
}
// Case B: Simple race with response for simple page
else if (url.pathname === '/about') {
const fetching = fetchRequest(request);
result = Promise.race([
fetching,
self.requests.match(request).catch(() => fetching)
]);
}
// Case C: Canceling huge **response**
else if (url.pathname.endsWith('.mp4')) {
self.requests.match(request).then(res => {
res.body.cancel();
});
result = Response.error();
}
event.respondWith(result);
return;
}
// Handle preloads, put them to the `self.requests` if aren't already there
if (preloads.indexOf(url.pathname) !== -1) {
// Check in existing requests
const result = self.requests.match(request).then(res => {
// If so, use it
if (res) return res;
// Check in caches next
return caches.match(request, {
cacheName: 'cache'
}).then(res => {
// If so, use it
if (res) return res;
// Fetch from the network and put to the cache
const fetching = fetch(request).then(res => {
if (res && res.ok) {
return putCache(request, res).then(() => res);
}
return res;
});
// Put preloads to the store so it could be accessed if something,
// e.g. if this is preload request from `Link:` header and the asset
// will be requested from the page later
self.requests.putUntil(request, fetching);
return fetching;
});
});
event.respondWith(result);
return;
}
// Handle normal and preload requests. They are going here too
// and if page requests same preload assets which are now _ingligh_
// then the same request is reused.
const result = self.requests.match(request).then(inflight => {
return inflight || self.caches.match(request);
}).then(res => {
if (res) return res;
return fetch(request).then(res => {
if (res && res.ok) {
putCache(request, res);
}
return res;
});
});
event.respondWith(result);
});
function preloadAssets() {
preloads.forEach(asset => {
const req = new Request(asset);
self.requests.match(req).then(res => {
// If inflight -- ignore
if (res) return;
self.caches.match(req, {
cacheName: 'cache'
}).then(res => {
// If already preloaded -- ignore
if (res) return;
const fetching = fetch(req).then(res => {
if (res && res.ok) {
return putCache.then(() => res);
}
return res;
});
self.requests.putUntil(req, fetching);
});
});
});
}
function fetchRequest(request) {
return caches.match(request, {
cacheName: 'cache'
}).then(res => {
if (res) return res;
return fetch(request).then(res => {
if (res && res.ok) {
putCache(request, res);
}
return res;
});
});
}
function putCache(req, res) {
return caches.open('cache').then(cache => {
return cache.put(req, res);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment