Service Workers

Main Thread

Service Worker

Web Worker

Worklet

Service Worker

if ('serviceWorker' in navigator) {
   navigator.serviceWorker.register("/serviceworker.js");
}
<html>

  <head>
    <title>The best website ever</title>
  </head>

  <body>
    <script>
      // Register the service worker
      if ('serviceWorker' in navigator) {
        navigator.serviceWorker.register('/sw.js')
        .then((registration) => {
          // Registration was successful
          console.log('SW registration successful with scope: ', registration.scope);
        })
        .catch((err) => {
          // registration failed :(
          console.log('ServiceWorker registration failed: ', err);
        });
      }
    </script>
  </body>

</html>
<html>

  <head>
    <title>The best website ever</title>
  </head>

  <body>
    <script>
      // Register the service worker
      if ('serviceWorker' in navigator) {
        navigator.serviceWorker.register('/sw.js')
        .then((registration) => {
          // Registration was successful
          console.log('SW registration successful with scope: ', registration.scope);
        })
        .catch((err) => {
          // registration failed :(
          console.log('ServiceWorker registration failed: ', err);
        });
      }
    </script>
  </body>

</html>
self.addEventListener('fetch', (event) => {
	if (/\.jpg$/.test(event.request.url)) {
		event.respondWith(fetch('/images/unicorn.jpg’));
	}
});

sw.js

addEventListener("fetch", (event) => {});

onfetch = (event) => {};
if ("serviceWorker" in navigator) {
  navigator.serviceWorker
    .register("/sw.js", { scope: "/" })
    .then((registration) => {
      // registration worked
      console.log("Registration succeeded.");
      registration.unregister().then((boolean) => {
        // if boolean = true, unregister is successful
      });
    })
    .catch((error) => {
      // registration failed
      console.error(`Registration failed with ${error}`);
    });
}

unregister()

Caching strategies

Cache first

Cache first

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  // Check if this is a request for an image
  if (event.request.destination === 'image') {
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Go to the cache first
      return cache.match(event.request.url).then((cachedResponse) => {
        // Return a cached response if we have one
        if (cachedResponse) {
          return cachedResponse;
        }

        // Otherwise, hit the network
        return fetch(event.request).then((fetchedResponse) => {
          // Add the network response to the cache for later visits
          cache.put(event.request, fetchedResponse.clone());

          // Return the network response
          return fetchedResponse;
        });
      });
    }));
  } else {
    return;
  }
});

Cache only

Cache only

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

// Assets to precache
const precachedAssets = [
  '/possum1.jpg',
  '/possum2.jpg',
  '/possum3.jpg',
  '/possum4.jpg'
];

self.addEventListener('install', (event) => {
  // Precache assets on install
  event.waitUntil(caches.open(cacheName).then((cache) => {
    return cache.addAll(precachedAssets);
  }));
});

self.addEventListener('fetch', (event) => {
  // Is this one of our precached assets?
  const url = new URL(event.request.url);
  const isPrecachedRequest = precachedAssets.includes(url.pathname);

  if (isPrecachedRequest) {
    // Grab the precached asset from the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      return cache.match(event.request.url);
    }));
  } else {
    // Go to the network ???
    return;
  }
});

Network first

Network first

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  // Check if this is a navigation request
  if (event.request.mode === 'navigate') {
    // Open the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Go to the network first
      return fetch(event.request.url).then((fetchedResponse) => {
        cache.put(event.request, fetchedResponse.clone());

        return fetchedResponse;
      }).catch(() => {
        // If the network is unavailable, get
        return cache.match(event.request.url);
      });
    }));
  } else {
    return;
  }
});

Network only

Network only

const networkOnly = (event) => {
 event.respondWith(fetch(event.request));
};

Stale while revalidate

Stale while revalidate

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  if (event.request.destination === 'image') {
    event.respondWith(caches.open(cacheName).then((cache) => {
      return cache.match(event.request).then((cachedResponse) => {
        const fetchedResponse = fetch(event.request).then((networkResponse) => {
          cache.put(event.request, networkResponse.clone());

          return networkResponse;
        });

        return cachedResponse || fetchedResponse;
      });
    }));
  } else {
    return;
  }
});

Service Workers

By Joan León

Service Workers

  • 384