Offline Now(ish)!

Alex Russell <slightlyoff@google.com>
Sept 17th, 2014
@slightlylate

<a href="...">
"Kernel Machine" by Alisneaky - @CC0 commons.wikimedia.org/wiki/File:Kernel_Machine.png
  :link
  :hover
  :active
  :visited
flickr.com/photos/monado/4405843023
flickr.com/photos/earcos/4812384484/
flickr.com/photos/evanblaser/5573908825
Click here.
flickr.com/photos/97477946@N00/8396969963

To Do Better We Need To Make A Break

Apps are Shells + Content

Registering a Service Worker

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/v1/static/sw.js').then(
    function(regstration) {
      console.log("will match",        registration.scope);
      console.log("still installing?", registration.installing);
    },
    console.error.bind(console)
  );
}

Having a Chat

if ('serviceWorker' in navigator) {
  // Ensure registered
  navigator.serviceWorker.register('/v1/static/sw.js').then(/* ... */);

  navigator.serviceWorker.ready.then(
    function(registration) {
      registration.active.postMessage("Howdy!");
    }
  );
}

Populating A Cache At Install

// v1/static/sw.js
this.addEventListener('install', function(e) {
  e.waitUntil(
    caches.create('acme-v1').then(function(cache) {
      return cache.addAll([
        '/v1/',
        '/v1/static/css/all.css',
        '/v1/static/js/all.js',
        '/v1/static/imgs/spalsh.svg',
        '/v1/static/imgs/icon.png'
        // ...
      ]);
    })
  );
});

Caches

Handling Requests

this.addEventListener('fetch', function(e) {
  console.log(e.request);
  console.log(e.request.url);
  console.log(e.request.method);
  console.log(e.request.headers);
});
this.addEventListener('fetch', function(e) {
  e.respondWith(/* some Promise for a Response */);
});

Handling Requests Programmatically

// A Passthrough Proxy
this.addEventListener('fetch', function(e) {
  // Stops dispatch of `fetch` event and goes to network immediately
  e.respondWith(fetch(e.request));
});
// Programmatic content
this.addEventListener('fetch', function(e) {
  e.respondWith(
    new Response(         // Constructor
      "<h1>OH HAI!</h1>", // String or ByteArray
      {
        statusCode: 200,
        headers: { /* ... */ }
      }
    )
  );
});

Handling Requests From The Cache

this.addEventListener('fetch', function(e) {
  e.respondWith(
    // check the cache first
    caches.match(e.request).then(function(response) {
      return response ||       // if it's in the cache, return it
             fetch(e.request); // else, go to the network
    })
  );
});

Read-Through Caching

this.addEventListener('fetch', function(e) {
  var url = new URL(event.request.url);
  // Don't cache if not a CDN request
  if (url.hostname == "static.acme.com") {
    e.respondWith(
      caches.match(e.request).then(function(response) {
        if (response) { return response; }
        return fetch(e.request).then(function(r) {
          return caches.get("acme-v1").then(function(cache) {
            return cache.put(e.request, r.clone()).then(function() {
              return r;
            });
          });
        });
      })
    );
  }
});

How Much Better Can We Do?

What It Takes To Get There

Application Opportunities

Performance Opportunities

Pre-load-scanner implications

Finer Points

Your Worker Will Be Killed
And That's OK

Implementation Status

Near-Term Work

Thank you for your attention!

Questions?

@slightlylate