 Hello, hopefully you've been having a wonderful conference so far. I certainly have been, lots of wonderful people have taken the stage before me, and lots of wonderful people will be taking the stage after me. So this is a talk about executing JavaScript across browser contexts. Now we are going to not be talking about GraphQL, I'm sorry, it just seemed like such a common thing, I just had to put the slide up here. Could I get the speaker clock running? That would be great. We're also not going to be talking about Node.js. When we talk about outside of the browser contexts, we often think about JavaScript inside of the command line, or outside of the browser. And that's not what we're going to be talking about today. We're also not going to be talking about browser extensions. So we're not going to be talking about things that are added by browser vendors. We're not going to be talking instead about these standard browser APIs. And these APIs are going to be both between tabs and outside tabs. So we're going to break the talk into these two main segments. So if you haven't noticed by now, by my liberal application of Denim, I am not from Spain. I am instead from the United States. I normally live in Washington, D.C., but I have been traveling for a little while. I've been traveling around lots of beautiful places in North America, specifically around the United States in the beginning, and dipped a little bit up into Canada. And one of the things that I've noticed while I've been traveling is, well, first off, that I work for a remote company. So this is wonderful. We're distributed in nine different countries across the world, which means that as someone working in its environment, you really need to write well. And one of the things that this also means is that you don't really need to hear well in order to be able to program for a remote company like this. And it's wonderful that we have conferences like this, and it's even more wonderful that they provide access to learning to all of you. And it's especially nice that they provide access to learning to people that can't necessarily hear as well as the rest of us. For instance, this wonderful person transcribing this or captioning this right now. And there is a friend of mine who sent me this tweet a while back, and it's a problem that not a lot of people talk about, and that deaf people don't really have the same level of access to conferences that we have. For example, can I see a show of hands? How many of you knew that this conference was going to be captioned before coming here? Yeah, so if you're deaf, that's a problem, right? You don't know that you can come to this conference and you have access to this learning. So I sat on this for a while, and I eventually came up with this thing, Conf Ally, but sometimes you spell it like that or like that or like that, but they all get you to the same place. And it's this very early project. GitHub doesn't even have a read me yet, and I'm telling you about it because it's something that I am passionate about and I would love for you to be passionate about as well. So if you'd like, there's plenty of pull request opportunities here. So let's get into the meat of the talk. Let's talk about standard browser APIs. So if you are like me, your development environment looks a little bit like this when you're working. You're in a browser, you've got a whole bunch of tabs open, and if you don't know, I work for this company called Harvest, and we were a time tracking company, so of course I'm tracking my time here. And also if you don't know, we have this thing that lets you add time tracking into apps, which isn't really that important except for the fact that, oh, I should say the apps, they look like this, you can write them yourself. The integrations look a little bit like this. We also have a Chrome extension that injects some of these integrations for you, and when that's the case, they look a little bit like this. So that's really not important except to say that when you're executing code, if we're doing our job right as a company, every single one of your tab is connected to our service. So you can see we've got some tabs from Harvest, we've got some from Trello, some from Basecamp, and some from GitHub as well. So that's a lot of tabs that are all syncing state with our servers. So this is kind of where we started on this project as a company. We're thinking, oh, well, what does this mean if, like, can we provide a better experience for people that are using all of these things? Because currently, we're relying on polling, which is not very ideal. We have a project to convert this to web sockets in the works. But that means that all of our tabs are slightly different. The state that you're seeing is a little bit different in each of these apps, which is unfortunate. So we started thinking about it. This is back in January. And ultimately, this is a terrible story to tell here because we ended up solving the problem with tweaking our cache control headers, and it was a single-line code change. But it got us thinking, right? There's more to the web than just a series of web pages. Have you thought of this before? There's really a lot more to the web than just web pages. But as developers, we hadn't really thought, hey, there's more we can do here. So when we start talking about this, we very quickly start talking about browser contexts. And you're probably thinking of this window object inside of your browser when I say browser context. But there's actually another set of, like, Sandbox area, and that's domain-level objects. So let me show you an example. This is what happens to me quite a bit. I'm watching a YouTube video, and then I get a little distracted, right? And I go and check Twitter, and I end up, you know, oh, like, this looks interesting, and I click through to another YouTube video. And then I'm like, oh, wait, I need to hear this. So I'm going to have to go over to my other tab. Oh, yeah, and I have to pause this video, and then I'll have to go back to this one. But now I've missed the beginning of the video, so I have to scroll back and then play it again, right? Does this happen to anyone else? This happens to me constantly, constantly. And so this could be better, right? This whole experience could be quite a bit better. And it could be better with cookies. Yeah, yeah. In the words of our speaker yesterday, it's back again, baby. Everything old is new again, right? So like, check out this picture of my dad in 1977, okay? Like, that could be a suit that I wear today, right? It could be, and look at that filter. Like, this could be Instagram today, right? So cookies, cookies, right? They're old, they're very old. But you can actually accomplish this with cookies, right? Writing cookies, reading cookies, painting the ass, got to use this weird regular expression because everything's encoded. And detecting changes to cookies is just polling, but you can do it. So please don't use cookies. It's just like making clear, please don't use cookies. You don't want to send a whole bunch of state data back and forth to your server when you make requests. But we replaced cookies with local storage, right? For this sort of use when you want to store something on the client. And if you've used local storage before, you might have come across this error, quota exceeded error. Has anyone received this error before? Show of hands? Wonderful. And another show of hands. Did you receive this error when you were storing a very small amount of data in local storage? Yes. Okay. So at Harvest, we started using local storage a while back. And we also used an exception tracker that keeps track of run-end exceptions so that we can keep an eye on if we've exploded things and that kind of stuff. We started getting a bunch of quota exceeded errors when we shipped the first version of our code that used local storage. And we started getting some people writing into support and they were like, hey, the app just doesn't load. And we're like, hmm, I wonder why? So we started looking into it. We followed up with these people. And it turns out all these people were using private browsing mode in Safari. And they didn't know they were using private browsing mode in Safari. Because if you've ever used private browsing mode in Safari, it's just a little checkbox in the corner where you can see all the videos in private browsing mode, which is a little, you know, hard to understand what's happening. And they implemented local storage in private browsing mode as having a quota of zero, which is why you get this exception. So this is kind of horrible, right? So this means that you can't rely on having local storage if you want your app to function in Safari private browsing mode. So that's why we have the cookie fallback, right? You can fall back to a cookie and do the same thing. Doing it with local storage is much easier. We've got set item, get item, and you have a listen. You can listen to a storage event to know when changes are happening. So let's go back to our YouTube example. How would we make this better using local storage? Looks like this. At least the implementation I chose to write. We just generate some identifier for our tab. In this case, we're just picking a random number. It doesn't really matter. And then we are going to set a playing tab whenever we play a video. And then in the storage event handler, what we do is we just check to see, every time this value changes, we check to see if it's our value. And if it's not our value, then we pause. This works great, right? You play in a new tab, the old one says, oh, that's not my tab that's playing anymore, and it pauses it. Now, I don't generally write code with this style. I just have to fit it on the page. So what we're seeing here is that this experience on YouTube, at least for me, and perhaps some of you, could have been improved with nine lines of JavaScript. So to me, this is a good example of why the web is not just a series of web pages, it's perhaps an idea that you don't really think about, right? Because we could have improved this experience quite a bit, and we just didn't do it. So that's a little bit about between tabs. Let's talk about JavaScript outside of tabs. So if you've ever used this before, you're probably thinking this is probably web workers. And it's not exactly web workers. There's a specialized form of web worker called shared worker, which you might not have used before or heard about. And if these are both new concepts to you, web workers work kind of like this. You have a tab, and it requests to run a background process, and it starts up a background process. You have another tab, it requests to run the same background process, it starts up another background process, et cetera, et cetera. They all have their own independent workers. Now with shared workers, they just all point to the same one. So the first tab that requests the worker, that one gets started, and every tab after that just connects to that worker. And if the first tab goes away, the worker still exists because one of the other tabs is connected to it. So this is great for our example in the beginning, right? When we're working in our development environment, we've got lots of tabs open. I remember all of that. Well, now it's okay. We can make it fine. We can just move all of our polling, all of our web socket connection into this thing called a shared worker. So this is kind of what it looks like, right? For us, we are an app that's running inside of an external page. So we've got something like a Facebook like button, that kind of a thing. You drop a little script in and it works. And then we have this external page. It talks to our harvest script. And then what would be really nice is if it could just connect to a shared worker. But the problem here is that since we're in someone else's domain, we instead have to spin up a handy old iframe, which then has to talk to our shared worker, which talks to our web socket, which gets a little complicated, but it allows us to significantly reduce the amount of load that we experience on our end. And it also allows the user to get a much better experience because all of their tabs are completely in sync. So this is what it looks like at the same time. This is great, except for this one fateful commit. This is in Safari. So back in January of 2015, it just got deleted. And the only thing that I can find publicly talked about is their platform status page, which just says it was imposing undesirable constraints on my browser, so they just deleted it one day and nobody reviewed it. So that's kind of sad for us, right? So shared workers, they're great for sharing processing and sharing resources like web socket connections. They're not so great for working in Safari. So that's shared workers. There's one other kind of worker that I would like to talk about. And that is a service worker. And service workers are a little bit more complicated. So I've devoted more of the talk to this and we'll talk a little more. And actually the upcoming talk, the one right after this, will actually go into more detail with service workers. So I'm kind of going to breeze over some topics and he'll go ahead and fill it in a bit. So I think one of the biggest problems with service workers is that they're kind of explained technically. I was chatting with a couple of people earlier on in the conference during the back-end days about Bitcoin. This is kind of unrelated to service workers. And we were talking about whether or not Bitcoin will succeed and stuff like this. And I was arguing that it's really hard for it to take off for the general public because you can't explain what it is without using the two words blockchain and that doesn't make any sense to average humans. It's like those words don't make any sense. And I think service worker kind of suffers from the same problem. The adoption hasn't quite taken off because it's kind of explained in interesting ways every time we talk about it. So, you know, on MDN, if you look it up, it says they're essentially a proxy and then later on it says, oh, but they actually also have access to push notifications and they also background sync things. It's like, wait, push notifications. That's not what a proxy does. Back on sync, that's not really what proxies do. It's not really like that explanation. In another place, they say that it's an event-driven worker registered against an origin and a path. And that explanation, I like that. It's pretty good, but it's still not ideal. I think we can do better than that. If we look at W3C, they say that it's a browser feature that provides event-driven scripts that run outside of web pages independently. And that's good, but I think we can still do a little bit better. The way I like to describe them, it's a process that's tied to domain events rather than a browsing context. So if you think about this, it's not tied to a browser context, right? So that means it's not necessarily running when you talk to your web page. And it also isn't necessarily... doesn't necessarily need your website to be running to have it be run, which is super interesting here. And it subscribes to a set of domain events. So what do we talk about when we're talking about domain events? Well, so we've got our normal worker... that normal worker context that gets started, right? A tab requests the service worker to be started. And then we also have these domain events over here that also can request service workers getting started. And those are things like push, sync, and fetch. And we're going to go into those one at a time. I mentioned before that the following talk is going to talk a little bit more about these events in detail. But if you have any questions that are more on the higher-level aspect then you can use our hashtag asksfsfs and they will be asked at the end. So let's talk about this push event. To me, this is the most exciting one. So we're going to talk about it first. This is kind of what the spec looks like. It's pretty new. You can see the latest draft is in July. And this is kind of what it is. So this... I'm going to click this button here in this demo page and it's going to pop up a little notification. Which is cool. Yeah, I get notifications. But notifications aren't new, right? We've had the notification API for quite some time. And so what's interesting is the button that I clicked. Send via XHR. So that means that we can send any sort of request to a particular URL and it should send us a notification. So what if we copy this URL command into a terminal and just pasted it? Well, it should send us a notification, right? And it does. Which is nice. It means that we can send notifications to browsers from our backend scripts. Things running on servers. Perhaps your Node.js, if you really want to talk about... GraphQL? So what's even more exciting here is that because the service worker runs independently of browser context, we can actually just close our browser and do the same thing, right? The browser doesn't matter for this to work. So we can go ahead and paste in a thing and it'll send us a notification. That's sweet. So the key here is that the context of the service worker is not tied to any web page. It's running outside of a page. So this obviously relies on some sort of a notification service. We're just curling a command somewhere. There's got to be some server listening on the other side. And because this spec is pretty new, this hasn't exactly been finalized. So if you're using, for example, Chrome, they use the Android notification service called Cloud Web Messaging or something like that. But you have to go into their DevTools or their developer login thingamajigger, whatever they call it, and request a little key and register an application and then copy some junk into a manifest. JSON is running on your server and then it's just complicated. If you're using Firefox for this, there's a little API in the browser that you just call out and do a thing. But I expect this to become much more standardized as the process goes on. We're not going to talk about this notification service too much, but just know it's similar to how a phone notification works, whereas if you have a phone that's through a vendor, that vendor has a notification service that you send messages to that get pushed down to the phone for performance reasons. So the basic way to start a service worker, you just call Navigator Service Worker Register, Returns of Promise and do stuff with it. And it's important to note here the lifecycle of this service worker as it's being installed. So the first service worker gets started, and if a new version of your service worker comes out, what happens is that when you visit the page, the browser automatically checks to see if that JavaScript file has a new version or not using standard HTTP cache techniques. And the thing that's kind of a gotcha that you might not notice is that if you start a new tab now, you actually get the old version. And this is because the service worker doesn't run two versions. So the first version is still in use by the old page, so it stays active until all of the tabs are closed. So we close all of our tabs, then the third tab after this gets the new worker. But you can skip that if you want. There's an API for that. If you listen to the install event and then call skip waiting, you'll just go ahead and take over all of the active tabs once the new version of the service worker gets installed. So that's handy. We're especially good for development because it's really frustrating and weird when you have a tab open and you open a new tab and you're trying to debug a thing and you refresh it a bunch of times and it's still not getting the new version. So a good gotcha to keep in mind. And then push events themselves inside of the service worker, it's similar to cross domain messaging, you just receive a push event and it has data in it. And of course, all the complicated stuff is the stuff I'm skipping. So that's the push event. And it wakes your service worker in order to process this event. So if... Because the lifecycle of the service worker is not tied to a tab and it's not necessarily running all the time, in matter of fact, the spec mentions that the service worker can be terminated multiple times a second. So keep that in mind. Just don't have any sort of state. And so it will be woken by the browser whenever one of these messages come in. Brows and Sport looks like this. Now we see the skip waiting feature that I used and down bottom is the push API in general. So let's talk about the sync event. This is what the spec looks like. But really, I like this documentation better, which is a blog post by Google, mainly because it links to this amazing video of a chameleon popping bubbles. It just keeps going. It's amazing. So the problem this API is trying to solve is a problem that we have pretty regularly. And I think it's probably something that you've experienced either within the sites that you manage or within other sites. So typically, we've got a time sheet and people typically don't like to stare at the time sheet. So they just go to their time sheet and they click a button and then try to close the window. But the problem is we haven't saved it yet. So we have to present that weird dialogue that says, oh, there are attendees saved. There are unsaved changes and then you're like, okay, wait, you have to wait. And this is not a very good experience. It's especially bad when you're on your phone. If you're, like, performing some action on the web on your phone and then you just have to wait and stare at the screen until a network request finishes, which it's silly that we haven't figured this out yet. And a lot of... I've actually heard this is a really good reason to move away from the mobile web and move to a native app because you don't have to have the app running to perform these sync events. But that's exactly what this API is meant for. So it looks kind of like this. After your service worker is ready and installed, you get a registration, access to the registration object and you call sync.register. And this sync time in this case is just a word. You just pass in whatever you want. Because what's happening here is that you're asking the browser to wake up your service worker at some point in the future whenever we have a stable internet connection. That might be now. That might be later. And because it's not necessarily tied to any particular point in time, you need to tell the service worker what to do at this point. Because you probably can't just say sync and have the service worker know what that means. So that's what that key is, sync time in this case. And then inside of the service worker, you just listen for that sync event and when you switch on the tag that you passed in and do whatever stuff you need to do. So this is kind of what the lifecycle looks like. The tab, which has started with the service worker calls the sync API passing in the sync time. And it tells the browser, hey, I want this thing to be run at some point in the future whenever we have a stable internet connection. Then all of those things can go away. You can close the browser, you can close your tab if you're on your phone, you can go back to browsing Twitter like I always end up doing, unfortunately. And then at some point in time, whenever you have a stable connection, the browser is going to call out to your worker that is the sync event. It's great for backgrounding a sync process even in limited or no connectivity environments. And here's what browser support looks like. Yeah. We'll get there. We'll get there. I believe we'll get there. Of course, the spec needs to do some work and... Let's talk about the fetch event. I think this is the one that people like to talk about most when they talk about service workers. Which is why I've left it to last. This is kind of what we're talking about. We are... We're going to access this demo page here and then we're going to disconnect from the internet. This is... So I live in Washington, D.C. Our subway system isn't quite as nice as the one here and I don't have network access. So what you see is that the first two pages load fine. The third one doesn't load at all. And that's because we're not connected to the internet. So what we're witnessing here is that the service worker has actually started in the background. It's prefetched some URLs and stored them in a cache. Then when the browser tries to access those pages when we're offline, it just serves them like normal. And if we hit a URL that we haven't prefetched, it's just normal disconnected from the internet behavior. So this is what that code looks like. Inside of your service worker, you just listen to a fetch event and then if you call event.respondwith, you're telling the browser, hey, don't do your normal behavior. I got this. I'll fulfill this request. We then check our cache. You notice this cache's object is a bit new. We now have programmatic access to the browser cache, which is something that we haven't had in the past. Typically when you're in a browser and something is cached, you just see the response as if it came from the server, but it didn't actually come from the server. In this case, you actually have access to the cache itself. And you can put things in, take things out, all those sorts of things. If the cache has this request in it, we just go ahead and return the response that we can pull out of the cache. Otherwise, we use the fetch. The fetch API is the newer version of XML HTTP request, the one that doesn't suck. We call into it and we just return the response we get from that. The fetch event, it's great. The programmatic access to the browser cache replaces app cache. If you've used app cache before, app cache is famous for allowing yourself to shoot you and it allows yourself to shoot yourself in the foot very, very easily. There's a wonderful article entitled app cache is a douche bag, which you can go ahead and read if you want to know why it's such an awful thing. It's very, very useful. It's actually what the Chrome new tab page used to use, and I think other browsers' new tab pages as well. They have since shifted over to use service workers, but it's great for allowing a page to be accessible offline. But it's also way more complicated than it needs to be and you can really easily accidentally cache the file that tells the browser how to cache things, which then means that everything is cached indefinitely forever and you have to delete a file off of your file system in order to get it to work again. Yeah, I'm really glad this is easier. It allows custom offline logic, so for example, if you, because you're intercepting the request between the browser and when it actually gets made, you can say, well, go ahead and make that request, but if it doesn't respond within one second, I'm just going to serve the cached copy that I have and then when that response comes back, I'll store it in the cache so that next time we have access to that. Whatever you want to do. It's also completely encapsulated, so from the website's perspective, you're just making a normal request. Maybe you're making a request with XML HB request, maybe using the fetch API, maybe using jQuery, whatever you're using, the request just appears normal. You have no idea that it's being handled by the service worker unless you specifically look for it. This is what browser support looks like. A lot better. So that is push, sync, and fetch inside of a service worker. And service workers themselves have a few caveats which I have kind of touched on a little bit. First is that they're only accessible over HTTPS or local host. They're definitely not persistent. We've talked about this. They can be terminated multiple times a second by the browser. They will be alive for you to handle the events that you have, but once the process goes idle, there's no guarantee it'll be running. It can be started by one page and then shut down and then started back up by that page and then shut down. It can also be started by things that aren't in the page. There are events that come across your domain like push, sync, or fetch. So hopefully in this time that you're listening to me, you believe this statement as much as I do. There's more to the web than just pages. The web is this wonderful platform. It's the only platform that I'm aware of. Feel free to correct me. It's built on a series of open standards which is wonderful and amazing. I think it's a very valuable resource and I would love to protect it, especially as it develops beyond just HTML sitting in a file server somewhere and instead turns into this nice, wonderful platform. So I mentioned a few times. I work for this company called Harvest. We do have an opening in DevOps which is probably not what you're into, feel free to take a look. And the slides to this talk, as well as the access to further resources about all the things I've talked about, including the code samples that I showed earlier, are available at www.dunkman.me. I'm A-Dunkman on Twitter and I have also tweeted the details to this I'm going to in about four minutes. So if you'd like to get the link from there, you can. Thank you. What security measures are in place to avoid people abusing all these new features like service work, et cetera? So the biggest thing is that there's no guarantee that the service worker is running for you, right? So for instance, I have this problem in Chrome every once in a while where you send in a notification and it just doesn't. And so the problem is that I haven't visited the that site often enough. So Chrome keeps track of how often you visit a site and it doesn't just let a site consistently send you a bunch of notifications because that would be kind of annoying. So I haven't actually looked into the rules that it uses, but it does mention in their documentation that, oh, well, it might just not happen if you don't go to that site often enough. And then the fact that it's only accessible over HTTPS is very important because the browser automatically checks to see if there's a new version of your service worker available. This can happen even if you're on an untrusted network, right? And so you don't want your service worker to be upgraded to a version that's produced by someone else. You're very vulnerable to man-in-the-middle attacks which is why it's only available over HTTPS. A lot of people have said to me at different conferences that service workers seem absolutely brilliant. But what's slowing down adoption is the requirement for HTTPS because if you've got like a huge website, moving it over is quite hard. Do you hear that said? I don't hear that very often. I think the web is definitely pushing towards HTTPS. So when I came into the country I bought a Vodafone SIM card for my phone and I've been using it for tethering and it's actually been injecting scripts into every page that I visit that's not over HTTPS. And I think that this is becoming a more and more common practice, unfortunately. And so I think it's kind of pushing HTTPS you know, service workers are the thing that drives it. So I think that's an argument that will quickly fall out as people more and more people upgrade. Gotcha. And this is the question from the audience, not from me. I want to make that clear because I work for Opera, which is a browser, but do you feel that Safari is the new IU6? Could you put a spotlight on him? Is that possible? No. And let me tell you why. So I believe that that IU6 and particularly older versions of Internet Explorer, I think Microsoft is doing much, much better at this, but I think the reason why we love to hate them is that they were never updated, right? The web as a platform moves very quickly and the problem with these is that like IU6 was on this release schedule that was tied into the operating system, which is you know, a pretty bad idea. We've come to realize that and Microsoft has come to realize that. You know, they've shifted away from their normal Internet Explorer releases over to Edge, which is constantly upgraded. And I feel like to call Safari, you know, put Safari in the same bucket is unfair because to me, I don't believe that Safari is lagging behind because they're not trying. I don't believe they're lagging behind because of their release schedule. I think they're lagging behind because they have this very tiny focus on battery performance. I think, to me, I would much prefer it to be continually upgraded and have a little bit poorer battery performance, but I know a lot of people use Safari specifically because of its better performance. And I think that's where they're trying to edge another market. And so if they can't find a way to implement the feature they want without continuing their legacy of a pretty good battery performance, then they just skip the feature or wait until they can implement it better. But of course, service workers, if the browser doesn't support them, the site behaves as normally, but just doesn't work offline. Yes. So it's progressive enhancements. Yep. Yeah. And you can check for the presence of the service worker object inside of Navigator, which is how you can detect whether or not the service worker is available. Excellent. Thank you. Give it up for the entirely sober Andrew Dunkman.