Skip to content

Instantly share code, notes, and snippets.

@jeffposnick
jeffposnick / index.html
Last active August 25, 2020 11:50
Exploration of how a service worker's fetch handler affects the DevTools Network panel
<html>
<head>
<title>DevTools Test</title>
</head>
<body>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js');
// Wait until the SW has taken control of the page before inserting the <script> elements.
// That way we can be sure the SW's fetch handler will intercept them.
import {RouteHandlerCallbackOptions} from 'workbox-core/types';
import {CacheFirst, NetworkFirst} from 'workbox-strategies';
import {registerRoute} from 'workbox-routing';
// Borrowed from https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook#cache-and-network-race
function promiseAny(promises: Array<Promise<Response>>): Promise<Response> {
return new Promise((resolve, reject) => {
promises = promises.map(p => Promise.resolve(p));
promises.forEach(p => p.then(resolve));
promises.reduce((a, b) => a.catch(() => b))
@jeffposnick
jeffposnick / totalCacheSize.js
Created October 11, 2019 19:12
Snippet to get the total size of everything in the Cache Storage API for the current origin
async function totalCacheSize() {
let size = 0;
const cacheNames = await caches.keys();
for (const cacheName of cacheNames) {
const cache = await caches.open(cacheName);
const cachedRequests = await cache.keys();
for (const cachedRequest of cachedRequests) {
const cachedResponse = await cache.match(cachedRequest);
const responseBlob = await cachedResponse.blob();
size += responseBlob.size;
@jeffposnick
jeffposnick / new-service-worker.ts
Created October 6, 2019 16:54
Workbox CDN + JS service worker migrated to a custom bundle + TS equivalent
import {CacheFirst, NetworkOnly} from 'workbox-strategies';
import {cacheNames} from 'workbox-core';
import {cleanupOutdatedCaches, getCacheKeyForURL, precacheAndRoute} from 'workbox-precaching';
import {ExpirationPlugin} from 'workbox-expiration';
import {initialize as initializeOfflineAnalytics} from 'workbox-google-analytics';
import {registerRoute, setCatchHandler} from 'workbox-routing';
import {RouteHandlerCallbackOptions} from 'workbox-core/types';
import {skipWaiting} from 'workbox-core';
import nunjucks from 'nunjucks/browser/nunjucks';
@jeffposnick
jeffposnick / app.js
Created October 5, 2017 20:06
Example of staleWhileRevalidate + broadcastCacheUpdate
const HN_URL = 'https://hackernewsapi.example.com/';
function updateUI(hnData) {
// Update the DOM.
}
async function init() {
const channel = new BroadcastChannel('hn-updates');
channel.addEventListener('message', async (event) => {
if (event.data.payload.updatedUrl === HN_URL) {
@jeffposnick
jeffposnick / camino.sh
Created December 19, 2018 15:17
Shell script to automate nightly updates for the Camino browser (circa 2003?)
#!/bin/sh
echo "Beginning Camino Nightly Download: `date`" >> /Users/jeff/Documents/Camino/log.txt
echo "Changing to download folder" >> /Users/jeff/Documents/Camino/log.txt
cd ~/Documents/Camino
echo "pwd is: `pwd`" >> /Users/jeff/Documents/Camino/log.txt
echo "Downloading Camino: `date`" >> /Users/jeff/Documents/Camino/log.txt
@jeffposnick
jeffposnick / find-opaque-responses.js
Created December 2, 2018 17:39
Code to find all opaque responses currently cached, and return their URLs.
async function findOpaqueResponses() {
const cacheNames = await caches.keys();
const urlsResultingInAnOpaqueResponse = [];
for (const cacheName of cacheNames) {
const cache = await caches.open(cacheName);
const requests = await cache.keys();
for (const request of requests) {
const response = await cache.match(request);
if (response.status === 0) {
urlsResultingInAnOpaqueResponse.push(request.url);
@jeffposnick
jeffposnick / index.html
Last active July 26, 2018 20:05
Testing manifest.json interception by a service worker.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Manifest Test</title>
<link rel="manifest" href="manifest.json">
<link rel="icon" href="icon.png">
</head>
<body>
I'm just a test file.

After thinking about this a bit more, and reading Paul Lewis's article about rIC, I'm of the opinion that it's not the right fit.

rIC seems geared towards delaying a discrete piece of work, which will happen on the main thread, until there are a few free moments.

In our case, the expensive bits all happen in a separate thread, at some indeterminate future point in time, after the serviceWorker.register() has already completed.

rIC doesn't offer any guarantees that starting up the SW thread and precaching resources will happen when the main thread is idle, and I don't want to give developers the false impression that it does.