JAKE ARCHIBALD: Hello. I'm Jake Archibald, off of
the Chrome Developer Relations Team. And I want to show you
something pretty incredible. Now don't adjust your set. What we're looking at here
is the browser establishment connection. It's an incredible
feat of technology. Data is being sent through
the air despite low signal, then through cabling to the
other side of the world. And yet, here comes the
response making that final leap through the air. Oh, it didn't work. Network connectivity is
a single point of failure when it comes to user
experience on the web. When connectivity is bad,
user experience is bad. When connectivity is gone,
user experience is gone. Native apps can overcome this. Well-built native
apps get cached stuff on the screen straight away,
and then they go to the network. And if that fails,
sometimes they don't even need to tell the
user something went wrong. This pattern is offline first. And I want to tell you about new
API that brings it to the web. PAUL: Isn't that
what appcache is for? JAKE ARCHIBALD: Yes, good point. Thanks, Paul. Appcache did try and
fix this problem. It used a man-- this
is your bit, done. You're good now. You can go. PAUL: Oh. JAKE ARCHIBALD: It used
a manifest format that was really simple, but
resulted in complex changes to the networking and caching
behavior of your pages. Well, I say complex. As developers, we deal with
complex code all the time. But in appcache's case,
it feels like magic. Because there's little
to link the lines in the manifest to the bits
of this behavior it triggers. The specific problems
with appcache are pretty well documented. But here's a quick recap. [MUSIC PLAYING] Well, we should all be
on the same page now. But it's a shame about
appcache, because there's some powerful stuff in
there-- caching, routing, updating, offline first,
online first, handling failure. But those tools can only be
used in very particular ways. There's no expression of
intent in the manifest. This makes it really
easy to be caught out by unexpected behavior. And your only line of
defense is to read the spec. Read it until your
eyes bleed, and then wipe the blood from your
eyes, and read the spec again. The solution to this
is the ServiceWorker. This is a new spec developed
by Google, Mozilla, Samsung and others. There's the spec. You can see why I went
for the fireworks instead. But the ServiceWorker
breaks appcache down into a set of
primitives that you can reason about and
use independently. And no behaviors change
until you change them, using JavaScript. Google and Mozilla
are aggressively working on implementations
of ServiceWorker. Some of the things I cover
may be buggy or not yet implemented. Check out this site for the
current status in each browser. Right, let's take a look
at a real-world use case. This is Trained to
Thrill, a simple website I threw together. It delivers you the very
latest in hot railway action, straight from Flickr. I'm going to add it
to my home screen, because it feels like
that kind of thing I might need urgent access to. And it gives it that
native app feel. It's all on GitHub,
so you can check out and run the final
implementation. I don't know why I made
something about trains. It just came to me as
I was standing outside, admiring some trees. Unfortunately, this is our
current offline experience. Things are great online. But without a connection,
we get total failure. Let's do something about that. In our site's code, we'll
register for a ServiceWorker. I'm using feature detection
here so I don't get errors in browsers that don't yet
support ServiceWorkers. This is the URL to the script
that will control our pages. And this is the range
of pages it'll control. Register returns a promise,
so you can call .then and pass in success and
failure callbacks. Promises are a
really powerful way to control async operations. To learn more about
promises, check out this article on HTML5 Rocks. Because of the control you
get with a ServiceWorker, you can only register
for them on pages that are served over HTTPS. GitHub does this, so
it's a great place to host experiments
and little apps. So what happens to sw.js? Well, it runs in a
different thread, and it has no DOM access. It's a place where you can
run code outside of pages and control their loading. One ServiceWorker can
control many pages. You can think of it
as like a proxy server sitting on the client,
able to re-route requests or satisfy them
from its own cache. Let's do some of that. The first thing we care
about is the install event. This fires when the
browser sees this version of the ServiceWorker
for the first time, passing a promise to
wait until it defines the length of the
installation process. If the promise rejects,
the install fails, and this worker won't
do anything else. If that happens,
don't worry about it. You'll get another
shot at it next time you call ServiceWorker.register. We're going to use the install
event to cache everything we need to load the page. Cache is a new storage API
provided by the ServiceWorker. It's basically a
programmable HTTP cache, but things never expire. They're only gone
when you remove them. We'll create a new
cache and add it to the cache's object
to give it persistence. The cache's object is a
global within ServiceWorker. Then we'll add items to
the cache, everything we need to load the site. These methods return promises
so they can be used to feed [? wait ?] [? until. ?] If any of these
things fail to cache, the promise rejects
and the install fails. So if the install
succeeds, we know we have all of these
items in the cache. Although we've cached
a lot of stuff, the browser won't use any
of it until we tell it to. You can listen for
the fetch events. This fires for
every page request within the ServiceWorker
scope, but also for requests made
by those pages, even if they're
to other domains. You get a lot of information
about the request, such as URL, method, headers. However, the most interesting
bit is you can hijack the event and respond to the
request yourself. RespondWith takes
a response object, or a promise for a response. You could create the
response yourself from scratch using the
response constructor. But it just so happens we've
got a cache full of stuff we can use. Caches.match takes a
request and gives us a promise for a
response, a response that matches the request. The matching is
done by URL method and vary headers, just
like the HTTP cache. Let's see how that runs. Oh, we've broken it. Connectivity derailed. Because it's trains. Even with full
connectivity, it's broken. In fact, if we take
connectivity away, we get exactly the same results. Our page is working offline, but
all communication with Flickr is failing, even online. This is because our
request to Flickr, like all in-page requests,
hit the fetch event. And then it tries to get
a response from the cache, doesn't find one. The promise rejects,
and we get back what looks like a
connection failure. You see, we've told it
to satisfy all requests from the cache. And we didn't cache
anything from Flickr. Thankfully, promises let
us recover from this. If our cache lookup
fails, we can use event.default, which
goes to the network to fetch the resource. That might fail as well,
of course, in which case we can catch again
and serve a fallback. And that will never fail,
because we depended on fallback to HTML in our install step. Now everything
works again, well, as long as you've
got a connection. If we disable connectivity,
we're back here. So we're pretty close. We just have to cater
for Flickr's absence when we're offline. But to do that, before we do
that, let's take a step back. This is what we have so far. And it just so happens
that we're still roughly within appcache's capabilities. But to do this in appcache, our
manifest would look like this. It looks a lot simpler, but
it's not actually simpler. It's just fewer characters. This doesn't involve us in all
the behaviors it's created, the caching, the
routing, the fallbacks. If this Service Worker does
something we don't expect, if it behaves in a way we don't
understand, what can we do? Well, it's JavaScript. We can use console.log. We can open dev tools,
set breakpoints, see the state of everything
as it goes through the worker. If this does something we don't
expect, and it probably will, we're back to reading the
spec again, crying blood. But enough about appcache. Let's do something it can't. Let's make that Flickr data
and imagery work offline. Here's what our page
is currently doing. We request photo
data from Flickr. And if that works, we update
the page with that data. Otherwise, we show an error. Either way, we stop that
spinner spinning around. We want this to work without
a connection, which obviously means caching Flickr's
API response and images. However, if the
user's online, we want them to get the
very latest locomotive delights Flickr has to offer. It's tempting, then,
to go for something like this, where we try to
get train photo data online. And if that fails, we
go for the cached stuff. For some apps, this
is the best approach. But it's not good
enough for our trains. You see, this works
fine when you're in a big city with full signal. Stuff arrives fast. It works fine when you're on
the underground, with no signal. In fact, it's even faster than
before because we fail straight away and go straight
to the cache. This is the problem case. Your phone sort of, kind
of, maybe has a connection, possibly. It'll either take
ages to get the data, or take ages to decide it can't. In fact, signal bars aren't
even a good predictor of this behavior. We can have full
signal, but we're still beholden to all of the
internet pipes that sit between us and Flickr. Prediction is really inaccurate
when it comes to the network. Take navigator.onLine, which is
available within ServiceWorkers as well as pages. But it's not all that useful. You see, it tries
to be predictive. When you have absolutely no
connectivity, it will be false. When you have some
connectivity, it will be true. Even if the route
you're connected to is just plugged into
some soil, it'll be true. Now our prediction is
flawed, be wary of it. With this code,
the network needs to succeed or fail
before we do anything. Instead, let's get stuff
from the cache straight away and then go to the network. This isn't a revolutionary idea. Native apps often do this. But now we can do it on the web. First, we change how we
fetch live data slightly. We stop handling errors. And we set a variable to true
if and when it completes. We make this request
race with another, one that's only going
to go to the cache. It sounds unbelievable that the
network might beat the cache. But some virus scanners
make disk access like wading through mud. So we should prepare for that. In terms of error
handling, we only want to show a connection
error if both the cache and the network fail to deliver. If the network fails but the
cache delivers, that's OK. We can fail silently. That's a good offline
experience in this case. But how do we make one
request to cache and another to the network? Here's how we make
a network request. The Flickr search
function is just going to use XHR to make
a get request to Flickr. Getting cache data is
a little bit different. It fails if ServiceWorker
isn't supported, or if there isn't one
being used by this page. The Flickr search
is almost the same. The only difference is
this additional header. It looks like
magic, but it isn't. In fact, it doesn't
do anything yet. But it's something we'll
see as part of the request to the ServiceWorker. And we'll add our
own magic in there. Back into ServiceWorker,
let's do some extra stuff in our fetch listener. If the request URL
is to Flickr's API, we'll do something special. If the request URL is to
Flickr's image service, we'll do something else special. First, let's look at how
we handle API requests. If the request has that header,
the one that we set before, we'll send back a match
from the cache only. If there's no match,
the request will fail. That's fine. That's exactly what we want. If that header
isn't there, we'll create a new cache
for our Flickr content and add it to our
cache's object. Then we'll pipe the
request to our cache, but also back to the browser. The fetch method takes
a request and returns a promise for a response. Although we're using this
request object twice, the browser is smart
enough to only make one request under the hood. This dynamic handling of
requests and caches is something appcache
simply cannot do. So that's API request sorted,
but what about the request to Flickr's image service? First up, we'll try
and serve the images directly from the cache. But if that fails, we'll get
hold of our content cache, and we'll add this
new image to it. We'll also respond with
it, send it straight back to the browser. This exposes another interesting
feature of ServiceWorkers. Images from Flickr do
not have cause headers. You can't fetch them with XHR. ServiceWorkers don't
have this restriction. You'll get back an
opaque response. You can't query its
content with JavaScript, but you can add it to caches
and use it in responses. And that's it. Now we have an app that loads
blindingly fast and updates with the latest content, if
the user has a connection, but also works
entirely without one. They'll just get the last
set of images they saw. It's fully offline first. We assume nothing
at the network. We render with what
we've got, and then we try the network and
deal with the outcome. Mission accomplished. And that's just one example. ServiceWorker gives
you the primitives to do what's best for your app. It doesn't force you
down a particular path. This is a very new
feature, but we're already seeing other
specifications extended to make it even more powerful. For example, background sync. It adds a sync event
to ServiceWorkers. This allows a ServiceWorker
to spin up even when the user
isn't at your site, giving an opportunity
to update caches. Ideal for sending that
message that failed to send, or updating a set of
train photos from Flickr. And push-- push allows
the Service Worker to spin up in reaction
to a server push message. You can use this to
update caches and notify the user they have a new
message, or, I don't know, something to do with trains. Anyway, ServiceWorker
isn't limited to trains. Hopefully you've
got better ideas. The W3 web mobile group are
currently compiling examples, and they're looking
for contributors. If you want to start
writing demos, or coming up with use cases, this is
a great place to do it. Making stuff work offline
is just one possibility. You can also use Service
Workers to fine-tune performance on powerful network APIs
such as client hints. If you're building
with ServiceWorkers, remember to use this page to
see what's actually working. See what you can
build in Chrome today. Of course, there will
probably be bugs. Please, file tickets
as you find them. Filing bugs and making examples
will improve our implementation and steer the
direction of the API. We're crazily excited about
the performance benefits and new features this
brings to the web. Let's build some
cool stuff with it. Thanks for listening.
I thoroughly enjoyed this. And got some knowledge.