Workers of the Web Unite

(upbeat electronic music) - Yeah, so today, title aside, John came up with it.

I'm not sure about it myself.

But I am here sorta to argue that you should have another look at web workers. If it's only just to inform your understanding of service workers, that's good enough.

But we should consider how web workers sorta, they've come a long way in the past few years. What I really wanna stress is today I'm not gonna give you a bunch of tutorials on how to implement like the best caching strategy for your client's application, because Jake Archibald has got that covered. He's done it quite well.

There's a bunch of resources out there.

But I want people to get sort of a conceptual understanding of what are workers, why do we have them in the first place? How...

It's one thing to say, "Yeah" offline.

But, how? Why? Why do we need this? Service workers are very hyped.

I mean, I love them, but I recently gave a talk at the Progressive Web Apps meetup here in Sydney. And my LinkedIn went out of control, like just following, just because the amount of people that were interested in the concept of Progressive Web Apps and how they get Progressive Web Apps.

And so like, you know, service work as well, it's interesting.

Yeah, it's...

Yeah, there's nothing on my LinkedIn.

Yeah, it's very popular and considering the history of the WeBWorK API, it's a bit sorta outta left field.

WeBWorK has been around for a very long time but never really sorta utilised too much.

The big one, yes, work offline.

We did have offline, we have offline capability and app cache, but it was arguable definitely too sorta high level.

But this is just sorta scratching the surface. Like what does it mean to work offline? Why is this important? And I think Alex Russell from Google.

He said this quite well.

So, "Service workers and PWAs: "It's about reliable performance, not offline." Supporting offline is just one thing that we can do to give the user a better experience.

And that's what it comes down to at the end of the day. It's about the user's experience.

So offline, I'm not offline that much.

But I often have like a sort of slow Internet connection. I live in Australia, so I think everyone in this room can at least understand that Internet's not amazing even in first world countries. So imagine if you're targeting a different demographics, where, you know, it's your daily pay goes to downloading a bunch of ad tracking scripts. It's something to consider.

Yeah, service workers, yeah, to think offline, it's just sorta just very narrow focus, it's putting on the blinkers, I think.

For me, it's sort of starting to come together the what a service worker is when I heard the term navigation controller. Those who don't know that service worker was actually originally pitched as a navigation controller.

So are there any iOS devs in the room? Yeah, so navigation controller's sorta is used in iOS development, but if you don't understand, if you haven't done app development, if you've worked with NVC, you just you have a controller, at the simplest sorta terms, just you have a controller and it accepts an input and you return a view.

Navigation could be your input.

History, location, is your input.

So that's sorta what it just comes down to. Based on a request, it goes to the controller and it returns a web view.

Now it's not called navigation controller, as you might have sort of figured out, but this sorta helps give us a firm, sorta like where we're sitting at the moment, like what we understand, where we started off. Web workers, no hype.

I think I'd say that.

Web workers they were before...

They were sorta pretty popular as in well-established before I even started my career.

So, yeah, so and hardly anyone's using it.

There's actually been talk to remove some aspects of it. I'd say there's shared workers which has never really implemented quite well.

But, yeah, so not very popular.

But we have the service worker that sorta is a web worker at the end of the day.

So, yeah, so this is still relevant, I think. And with all the work with service workers that's coming out we actually can have a look at what service worker's really sorta pushing what's possible with web workers just 'cause they have to. So this is just a base that we're working from. So service workers are web workers.

And there's also shared workers, which as I mentioned are not really widely implemented as WebKit, sorta just ripped out their implementation. But it's a good segue into service workers. Service workers draws quite heavily on the shared workers specification.

So to understand service workers, you have to understand what a web worker does. So what is it? Yeah, so it's vintage.

And it's supported so widely.

Internet Explorer supports web workers.

You can spawn new threads in Internet Explorer and Safari and Firefox and Chrome for a long time. And you have a parallel processing, but, yeah, like parallel processing.

It's easy to just throw that out there, throw out that term.

But why do we need parallel? What is parallel in this context? And then you're gonna hear this all the time like JavaScript is single-threaded, and it's really exciting! Like I heard that all the time especially when I was starting out.

And I knew that JavaScript was single-threaded. Cool, yeah, like, I don't know.

It's round, like JavaScript is single-threaded. It's just one of those things that you say to I don't know, say it at parties, you know, get kicked out.

But, yeah, so and single-threaded so like can just can only do one thing at a time and then JavaScript is asynchronous.

And that feels like we're doing lots.

It feels like that.

So single-threaded.

It's just one thing at a time.

That's it, right? But in JavaScript, we're doing 70 things all the time. So forget about JavaScript.

I don't know what people's background are.

But I've worked mostly in JavaScript for most of my career. So you kinda forget that other languages, they have this idea of this synchronous.

This is what we do.

So this is quite intuitive.

So we have a file.

We wanna read it and then we wanna do something with it. It's quite...

You can just read that like line by line.

It's simple and that's...

There's nothing wrong with it.

Like that's perfectly fine and that's sorta what you expect. But then you start with, say, you...

So this is the same thing.

It's sequential.

So you read it a file and then you do something with it and then you read another file and then you do something with that one.

But these two sort of files that just have no relation to each other, one's blocking the other.

So no one wants that.

No one wants to wait around.

So that's sorta the price that you pay with this model. And a solution to that is, you know, instead of that waiting, you can spawn a new thread. And that's...

And this does things at the same time.

So they don't depend on each other, they don't have to know anything about each other, but they just spawn a new thread.

That's it.

They do things at the same time.

And you worked with JavaScript at all you know that you don't need threads to sorta get the same, you don't need threads to avoid waiting at the end of the day, because most of us don't use threads at all. That's what the stats say and sort of in browser usage, because web workers, we have that available. But to achieve not waiting, no, we don't need that. This is what everyone does now.

We fetch and then do a response.

And this feels the same.

I think that's the issue, it feels the same. So we're okay with it.

And, again, this is different.

The results sorta works out similarly, but we're doing one thing at a time, 'cause JavaScript is single-threaded so we fetch and then we do.

And then after that, we get the response.

Then we convert it to JSON.

And these things are sorta not related and they don't necessarily block each other, but they do kinda, 'cause only one can execute at once. And usually that's fine.

That's generally okay.

So how that's done, that's Event Loop, that's another talk.

I definitely don't wanna talk about the Event Loop while the author of You Don't Know JS is sitting in the front row.

So it's just a cue, right? It's a cue.

We're still doing things one at a time.

But it's a cue.

The result is mostly the same.

It's not parallel, but it doesn't matter.

Like usually this is fine.

Again, just to reiterate, we have a solution. It works great.

You can go your whole life without knowing about threads. It's a win, I think.

So it's just got new threads.

That's all we have.

That's what workers do.

But I just told you we don't need threads to do this sorta thing.

We don't need threads to avoid waiting.

So, yeah, what would you want? If you could spawn threads, what would you do? Why not mine Crypto currency in the browser? And this is the perfect use case.

So you have low cache people got like idle CPUs, why don't you just add a minor and you can get some like, not Bitcoin, it's one of the newer fancier ones. But, yeah, like there's a service that you sign up for and you can get an API key and then you add that and then it basically mines your visitors for Crypto currency using their CPU.

It's an interesting use case for threads.

Probably I'm not quite selling it, yet, but I think I find like sorta like sorta evil things like this like quite inspiring in a way, like, "Wow, you can do that with JavaScript. "That's, yeah, like good for JavaScript." I guess a bit more sorta like closer in line with people's expectations and what they would actually do. Not everyone wants to mine people.

I mean, to each their own.

That's fair.

What about your framework? Who here uses React? Yeah, okay, there's...

Angular? Yeah.

Preact? Yeah.

Yeah, so these all use a virtual dom, right? And they're running and there's all this stuff that is happening all the time and it can block the main thread.

What if you just put all your framework code in a web worker? So this is a article on Performance Planet. So they found that more dom nodes that you have the better performance that you got by moving all your React or Angular logic to a web worker, because basically it's for free. Just because usually we could get by with this event loop. We can get a whole new event loop with another worker thread.

So, yeah, I think that's...

This is what I'm trying to sorta like...

There's lots of opportunities there.

Some of them worse than others.

But, yeah, so and then there's things such as, say, OffscreenCanvas.

Canvas is a dom API, but then there's OffscreenCanvas, which actually allows you to run Canvas operations in a web worker.

So this I find quite interesting, 'Cause I've done a lot of hackey...

I used to work in ad tech and the amount of inline video plays that use Canvas that would run on each frame, because you wanted to get around the autoplay. You wanted to autoplay on mobile, but you couldn't with a real video so you'd have to use a Canvas.

So, again, kinda evil, but maybe that's, I don't know, says something about me. But, yeah, so and you can just offload this potentially to a, like a worker that is solely concerned with just this one thing.

And that's interesting.

Like in other platforms there's really...

The idea of blocking the main thread is, you know, like that's terrible, everything, threads everywhere. And I'm not saying to go the other way, but we have to start exploring these opportunities. So you can just, on like Canvas, do things in a worker thread, and it's not too different.

So we don't have to worry too much about that sorta like it's the initial hurdle of thinking what should be parallel, like maybe it's like what doesn't what doesn't need to be parallel.

So, yeah, so just I find that's the issue.

I know I don't think in parallel.

And then there's WebCrypto.

That's another thing.

And they have fairly sorta newish sorta API just-- And this is super interesting that you could just do all this, you could like encrypt things in the browser and then offload it to a worker and then like not have to... And cryptography can be a little bit intense on using resources.

So, yeah, another opportunity.

That's all you need to do, just move things into threads. So how do we actually sorta get started with actually like practically, what's involved with workers? So consistently you'll see that there's a separate JavaScript file.

So you will have to sorta import, say, worker.js. Like it's not just inline.

It also has to have the same origin.

So that's basically those are, I think, fair. So that's it for a worker to get started.

What's interesting, actually, is there's module support coming.

It's in the specification.

But, yeah, and naming is just sorta more for debugging in the case of your standard dedicated worker, but with modules for workers you can actually import from other origins.

It actually gets rid of that sorta original sorta origin sorta scoping.

Yeah, so that's....

In the future we could use other people's workers. There's also the shared worker.

And I don't wanna spend too much time.

It's just mostly for that conceptual understanding like the path from our original sorta dedicated worker and sorta what they were trying to achieve with the shared worker.

So it starts off the same, but then you actually have to initialise on a port. Just because a shared worker, an individual thread, can actually have multiple connections.

So you have multiple pages.

And they can reference that.

You have multiple tabs opens instead of each spawning their own threads, they could just use the one.

So it's just more efficient.

That was the main sorta selling point.

But, yes, on thread, many support.

Like very little support.

And speaking to people that are working on the browsers, they weren't even too keen, well, five folks at least weren't that keen on these shared workers.

It's sorta like, "Oh, sounds cool." But, yeah, and but it did sorta pave the way. So the idea of just having multiple connections instead of just this one thread.

Now a new thing with service workers, and with most new technologies, anything nowadays, you want it...

It needs to be encrypted.

That's just how it is.

Bit less obvious is MIME type.

We need to have a tech/JavaScript MIME type. I mean, that's what you should be doing, but, yeah, that's the one sorta big difference. And they're trying to push that we should only be using JavaScript MIME types for JavaScript files, funnily enough.

But, yeah, like it's interesting how you can send an HTML file and usually they don't care. Yeah, so when look at the service worker syntax it's quite different.

We're working with the navigator now, so navigator on the window object.

And we get a service worker.

And then we actually want to register that service worker.

So it's quite far removed from the sorta new worker, sorta like very synchronous API.

Whereas this is starting to get...

So you can see that sorta transition from the ports, from the shared worker to the service worker. So, yeah, so this is just to get started.

So this gets you a service worker.

It's gonna print to the console.

I think that's all you need to be a progressive web app. Like as long as you tick some other boxes, but as long as you have that service worker file, like it's like, yeah, lighthouse, yeah, cool. But, yeah, but we can be a little bit more interesting. So I do run the creative coding way up in Sydney if you guys are keen to come.

But most of the creating coding, it's sorta like a bit broad.

For example, you could add a script and then call it, make sure you don't call it type, text or JavaScript, 'cause then that will actually execute. But you make it something that won't execute and then you create a new blob to generate this worker. So this is quite a sorta contrived example, but this is sorta like maybe you wanna generate a worker file.

I go back to this interesting CoinHive example. I looked at their code.

And what they do is they create an object URL based on response that they get.

So obviously they don't want people mining the wrong... Mining for the wrong people.

So they send their secrets in this one blob. I think that's kinda interesting.

Yeah, that's sorta roundabout way of going about this. And so stuff to just sorta think about.

So like if you think you're limited by this file requirement, yeah, just have a think, get inspired, by mining. Now I think the biggest concern for a lot of people is that web workers, they have a different scope. There's no window, there's no document.

It's a completely different world.

The scope is just entirely different.

So we don't have, there's no window anymore. So think about...

The window comes up more than you think, but sometimes less.

Document, so you can't query your dom.

And this is a good thing.

I mean, that blocks, you can't do anything with your dom without blocking the main thread really.

Yeah, so that's sort of the reason why.

But it takes some getting used to.

That's the biggest sorta problem.

You do still have navigator, but it's slightly modified.

There's not as much in it.

You still have access to location.

Set time out and clear time out.

You always forget that they're not actually part of like actual JavaScript language.

They're actually part of this browser context. As well as base 64 encoding, also, I forget about that. It does bite me sometimes. (chuckles) And XMLHttpRequest.

So XHR is deprecated.

So service workers can not use it.

That's the biggest change.

But you get to use fancy new fetch, which is much better, I think.

So this idea of like context.

So it's safe to say that sorta limitations, but there's an interesting journal article using the web worker to act as a jail for like your third-party script.

It can't do anything that you don't let it to, because it literally cannot access the dom. And that's a different approach, sorta like turning our perceptions into sort of positive. So it's the rule of least power, you know? Like if it doesn't need access to the dom, then don't give it access to the dom.

And this actually...

So they have a virtual dom.

This is from a few years actually ago.

Have a safe virtual dom, which you don't care about safety necessarily, but maybe you wanna control when dom updates like on your watch.

You don't wanna let some library to do all the... Take control over your application.

I think that's fair.

So that's, yeah, just another sorta benefit. If you wanna think of it as that way, just seeing these sorta detractions as more benefits. So how do we actually sorta communicate with these? So they obviously exist in their own global scope. So what's the connection? How do we actually communicate? How do we say hello? This is just standard web worker.

So if you have a document, you have to have sorta a page open and you start your worker, you import it and you send a message.

And you get one back.

That's it.

But the document, if you close the document, then your worker is gone.

So they're quite tied together.

That's all there is to it.

So it's quite simple.

It's the simplest interface that we have for communicating with workers.

And that's the simplest.

So postMessage.

So if you've sorta talked with iFrames that's some API.

Now on the worker, you add an event list now for on self, which is a reference to the global scope. And you get that message and that's it.

You could also emit the self just like you emit window, like you can do that. Up to you.

My personal preference is to keep the self, 'cause otherwise sometimes I forget.

But, you know, that's actually a global.

But, yeah, so then you have the shared worker. So we've sorta progressed.

So we have two documents.

And we could talk to the one shared worker. And that one shared worker can send messages to those two pages.

So if we got rid of one of these pages, then our worker, our shared worker could still carry on as normal.

It's business as usual.

And you could have multiple connections to the shared worker, but we're still tied to this document.

The document, at least one.

So if we close both documents, then the shared worker is gone.

But it's a step forward.

We're not sorta...

We're moving beyond just this single page model. And it's quite similar.

So you start the port before you post any messages or listen.

But it's basically the same.

That's it, you just communicate by the port. And one thing to note is actually the web worker does also use the port, but it's just implicit in that case. There's only one port that you're going to be using. There's only one.

So in the shared worker, so you actually listen for the connect message. So this is where it's a little bit more involved. So we actually get the port from the event object on the connection.

So we're getting a bit more asynchronous.

And then once that's done, we're just posting message again, just via this port.

And then within that port, yeah, you listen for those messages.

That's it at a glance.

So with the service worker, you initially have this document that sorta sets it up.

And you can communicate with it in the same way. You find that that's not really reasonable. I mean, you can if you want.

But it's quite similar.

This will listen for message on the service worker. It's not really...

You can use ports as well.

So that's where it gets a bit tricky.

It's not really something that you wanna use too much. You can listen for these messages on the service worker, but you could never really depend on the service worker doing what exactly you want at any particular moment in time.

Service worker just start...

They're quite separated from you.

They have their own sorta life cycle.

So we were determining the life cycle.

Or we could really have a good understanding of when a web worker started, 'cause we started it and we terminated it.

I feel bad saying that, but, you know, like terminating workers, but, you know. (chuckles) One thing to keep in mind, though, with messaging is there's actually work studies, well, some research and performance benchmarks of actually passing things through to these guys.

So you notice I actually sorta don't really. You can pass objects as well.

You could pass a Boolean if you like.

But it's actually found that if you pass strings, it's way more performant across most browsers most of the time.

So you're better off using a JSON.singer file than a JSON.pass whenever you're communicating via these ports.

But, yeah, so back to service worker, this is where sorta like the good thing about service worker comes in.

You can see this going from web worker to shared worker, so we can close one, if we closed one tab with the web worker, we're gone, that's it. Closed one of two tabs with the shared worker, we're still alive, but, two, we're out.

But now the service worker doesn't have to depend on this page at all.

And this is what makes it sorta really exciting, because in the web we have been really tied to this one sorta document model, even with people trying to force their way sorta to make it not that way with single-page apps and stuff.

But this is sorta, yeah, like we can look beyond the document.

Things can happen with us sorta being there, without our document being open.

And the service worker could still work.

It's 'cause it's event driven.

Unlike the others, I mean, there was the messaging event, but that's sorta, I mean, that's it.

That's how you interact with it.

That's all you have to do.

And you could do whatever you want with it really, but it's quite low level in that sense.

You have to sorta intervene control.

And now it is quite sorta, I guess, parallel in a way. So we have our...

So message and messageerror.

So that's across across sort of all the web workers. Then you have, yeah, you have the connect on the shared worker, but then you see how service workers actually got all these events that come up.

So this is just the beginning as well.

So instal and activate, fetch and foreignfetch, they're actually part of the service worker specification. But the service worker specification is also designed in a way that other specifications sorta build on top of it.

So like sync is from background sync and it's just the beginning.

So all these sort of we start with just these four events.

And then they're adding more and more all the time. So there's two lifecycle events.

So instal and activate.

That's the one that you sorta, you can do something about. So instal, that's only fired up that's once so in the lifetime of a service worker.

An instal occurs when you add a service worker. So you register a service worker file.

And a new instal occurs when we have the browsers and stuff do it, but by comparison of your service worker. That's to see if we have to instal a new one. So the sorta takeaway is don't check in your service worker into a cache.

It's a mistake that you'll make once.

And then until someone nukes this, sorta like service workers in the browser, they won't get anything new.

But, anyway.

So, yeah, so we could do...

This is just simple.

So this is, I mean, caches.

And it's like caches is actually a separate specification, actually, but sorta like really go hand in hand with the service worker.

We have event.waitUntil.

So this is a extension of the standard event. And what that does is it waits until, yeah, the promise is resolved.

So you see event.waitUntil sorta everywhere, sorta in service worker scripts.

That's essentially it.

It's just waiting until a resolution.

So, yeah, so then you can have your caches. You can open a new cache, give it a title, if you wanna version it.

Once that's opened, then you can add a list of static assets to a cache.

And that's to get started.

That's pretty simple, right? And then we also have activate.

So service worker, you never really sorta know when, never know for sure when it's actually up and running. So the instal is once, activate's sorta when something happens.

Like it's ready to actually do an action.

Install's sorta setting up, but activate, you can actually do things.

So the lifecycle's sorta that's...

Sorta like the start.

But this is, for me, like fetch is just...

This is how we do sorta...

This is just powerful, I think.

On any new request we can do whatever we want to it. That's it.

Like if we get a response and we can replace all the mentions of cats to dogs.

The reason for like service worker sorta it's very low level.

Browser's sorta learned from application not to really dictate in the same way like what you want to achieve.

So it's low level primitives, but that's great.

So we could build tools upon it, but this is it, like intercepting requests. Like we can check if we're...

Are we actually connected to the Internet? If we're not connected to the Internet then we give them something else.

That's it.

There's also foreignfetch, which actually, so run with fetch is just that it's sorta limited to your request.

But what bout other people's files? I know I don't request just my own stuff.

There's always like third-party scripts to include. So this is newer.

So this is sorta in origina trial in Chrome. But you can register for the foreignfetch on the instal event.

And that's sorta common with all these sorta functional events that you'll register for these new events.

And you can expect all origins.

Maybe I wouldn't do that, but you can.

And on foreignfetch, what it actually does is if they have a service worker on the site that you're requesting, then you can use their service worker.

If they've set up a cache, then you can use that without even sorta going to the Internet.

Sorta, yeah, powerful paradigm.

But, much newer, but I think it seems like people are quite keen on this as well.

So I think it's sorta...

We could start going on board.

Sync, another separate specification.

So background sync, and literally just are we online, are we offline? So this just this status update.

Like network connectivity has changed.

And when network connectivity has changed then we could do stuff.

So if we sorta tried to send a form but we're actually offline.

This is when we can actually start to get things done. WE can say, "Hey, you're back online." Or we can go, "Hey, oh, actually you're not online. "So we're gonna like warn you.

"Like, hey, like, let's do something about this, "but later on." And then there's push.

So then push is sorta like moving away from this idea of offline.

We could send...

We can create notifications.

We can tell the service worker, hey, we actually wanna give a notification.

And we wanna create a notification for the user in such a circumstance.

And then service worker we just wait for this push event. We can create a notification to show it.

And then we can handle what happens on the click. We can open the right window.

If they're actually in the browser, we can focus already on that client.

It's, yeah, low level primitives.

So these are the early sorta specs.

Push come quite along.

I'm sure it's, I mean, a bit annoying now, isn't it? A little bit, like all the push notifications. But, as a developer, yeah, let's...

I love power, you know? (chuckles) So this web workers I think we have control.

I always forget when I, like I've written this sentence. It just sounds so terrible, actually.

We ultimately have control over the life of the worker. Like I sound like a fascist like, like a dictator. But, yeah, so like there's high, so like we have to sorta intervene quite a lot.

Yeah, so we have a lot of control.

And I think that's why we've sorta struggled with web workers, like collectively, as sorta web developers.

'Cause we're quite event-driven.

Like we have to wait for a user input.

Like we usually don't do things until someone's clicked a button.

So without that, without these events, it's not quite the same.

And I think that's why sorta service workers are actually sorta been taken aboard, like sorta you have people are much more excited about it, which is great.

But I think we're sorta like looking past all this interesting stuff that we can achieve with web workers as well.

So service workers, it's a fire and forget. This just event driven for...

I think that's, yeah, I'm comfortable with that, I'm used to events.

One thing I just wanted to sorta wrap up with I guess is the idea of so these things are like APIs that have been added to the browsing context. So they're not actually part of the JavaScript language. But then you have new editions, like quite recent, SharedArrayBuffer and Atomics API. So what this does actually, so we can actually... This is part of like the sorta ECMA script, sorta like specification.

If we pass SharedArrayBuffer to our worker, we can actually, we don't have to do this post message stuff and listen for post message.

We can just sorta, we get it once, and then we actually shared the memory across threads, which is scary for reasons.

But we've added the Atomics API as well.

And so this is this idea of parallel is actually being brought into the JavaScript language, which hasn't really been seen.

This is sorta like a big deal.

Yeah, it's a big deal.

And I'm still sorta interested to see where this goes. Yeah, the idea of sharing things across threads. Yeah.

So there's a great article on Concurrent JavaScript can work.

So WebKit guys implemented SharedArrayBuffer. And they actually came up with this huge hypothetical like we can.

JavaScript isn't like very concurrent yet, but we could make it that way, which sounds airy fairy, but then it's like... Took me like an hour to read the whole thing. I think that's interesting.

So we're so used to these events, but the language itself is sort of moving to parallel. I don't know, like the powers that be really wanna go down this route.

So time to start embracing it, I think.

But, yeah, definitely give it a read.

If only for this one quote, which I will leave you with: "Therefore we can transition a flat butterfly "to a segmented one by allocating a spine "and pointing its fragment pointers "at the original flat butterfly." Yeah, I'll have to give it another read through. But there was a lot of talk of spines and butterflies and like replacing the spine.

But for parallel JavaScript, so if that's the price we pay, I mean, it is what it is.

So, yeah.

So thank you.

(audience applauding) (upbeat electronic music)