The ServiceWorker: The network layer is yours to own

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

I thoroughly enjoyed this. And got some knowledge.

๐Ÿ‘๏ธŽ︎ 2 ๐Ÿ‘ค๏ธŽ︎ u/gonzofish ๐Ÿ“…๏ธŽ︎ Jun 27 2014 ๐Ÿ—ซ︎ replies
Captions
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.
Info
Channel: Google Developers
Views: 72,483
Rating: undefined out of 5
Keywords: io14, iobyte, develop, chrome/web, tools, lon, jakearchibald, product: web, fullname: Jake Archibald, Location: LON, Team: Scalable Advocacy, Type: DevByte, GDS: Full Production
Id: 4uQMl7mFB6g
Channel Id: undefined
Length: 14min 54sec (894 seconds)
Published: Wed Jun 25 2014
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.