Exploring Service Workers

Message Handling in the Client

Kyle Simpson

Kyle Simpson

You Don't Know JS
Exploring Service Workers

Check out a free preview of the full Exploring Service Workers course

The "Message Handling in the Client" Lesson is part of the full, Exploring Service Workers course featured in this preview video. Here's what you'd learn in this lesson:

Kyle adds the ability to communicate with the service worker from the client by creating a function to send a status message through a channel port the service worker is listening on.


Transcript from the "Message Handling in the Client" Lesson

>> Kyle Simpson: We now have a working service worker, and we wanna start doing something useful with it. And one of the things that we're gonna need right off the bat is the ability to communicate with our service worker through messages, similar to how we communicated from the web worker to the page.

Now, when we did web workers in the earlier exercise, that was a dedicated web worker. And so all we had to do was self.postMessage, because there's only one page that that worker could be attached to. Because the service worker might be controlling multiple pages, it's a little slightly more complicated to do the messaging, and we're gonna walk through that.

So the first thing that we're gonna do is write a function to send a message to our service worker from the page. We're in blog JS, and we're gonna write a function called sendSWMessage. And I'm gonna pass it a target, which is an optional thing, and we'll see a little bit later why we have that.

So if the target's been specified, then we wanna say target.postMessage, sorry. We also need the message here, message and target, postMessage(msg);.
>> Kyle Simpson: Otherwise, if the (svcworker) has been set, then we wanna use that.
>> Kyle Simpson: And finally, if neither one of those has been provided, which shouldn't really happen.

But this is just sort of a fallback in case, then we wanna call navigator.serviceWorker.controller.postMessage(msg);.
>> Kyle Simpson: Okay, now how do we listen for messages from the service worker? We know how to send to it, but how do we listen to messages from it? We're gonna say navigator.serviceWorker.controller.addEventListener,
>> Kyle Simpson: ("message"), and onSWMessage, is the function that we'll define.

And actually, it's take out the controller part. It's a navigator.serviceWorker.AddEventListener. This one didn't end up needing to be an async function, so I'll take that off. Let's define an onSWMessage function.
>> Kyle Simpson: So I'm going to de-structure the event object that's coming in, because I know it's gonna have data property on it.

>> Kyle Simpson: But I'm also gonna need the event object itself, and you'll see why in just a moment. So if the data object that I've been set has a property on it called requestStatusUpdate, that's not a special thing. That's literally one that I'm making up. So we're gonna send a message from the service worker to the page, and I wanna listen for that kind of message.

So if that's what's been asked for, so basically, the server is gonna ask the page hey page, can you please give me an update on some stuff? What do we want for the service worker to know about? Well, specifically, when we say status update, we want the serviceWorker to know if we're online, and if we're logged in.

Now, the online part is actually discoverable in the service worker, because navigator.online is available in the service worker. But I have been unable to find any way to get the events in the service worker, the online, offline events. So my solution is have the page monitor it, and tell the service worker, okay?

And then for isLoggedIn, this requires accessing cookies, and the service worker definitely does not have access to cookies. So because we know there's at least some status that the page knows about that a service worker can't, then we need to know that information and pass it in. So when we say status update, we mean tell me if you're online, and tell me if you're logged in.

And the reason for that will become clearer later when we do our routing. But there are different things that we wanna do if somebody is online, versus not online, and that kinda thing. We wanna handle certain failure conditions differently, depending on those things. So we need to write a function to send those status updates.

>> Kyle Simpson: And that's gonna call our sendSWMessage. We'll send a message, and then we'll just pass along whatever target's been specified, if any. So our message needs to have isOnline, and isLoggedIn. So in our on service worker message, we received a request from the service worker to give us that data.

Why does the service worker need to request? Well, because the service worker might have been revived. So one of the things that we're gonna do whenever the service worker starts the first time is we're gonna say, hey page, tell me the status of these two things. Cuz it might have died, and we might be coming back.

Alternately, we could have been storing this stuff in, say, something like IndexedDB. But it seems like we're gonna need to communicate with the page anyways, so might as well just ask the page for the status. So we're gonna say sendStatusUpdate. Before we do that, just for our debugging purposes, I'm gonna put a console message here.

Received [INAUDIBLE] back to quote, we're gonna say Received,
>> Kyle Simpson: Status update request from service worker, responding. That's just for sanity checking. Okay, so we wanna send the status update, and who do we wanna send it to? Well you might think, well, we should definitely send it to the SVC worker.

Well, it turns out, because there's gonna be multiple places that we're communicating with, the service worker's communicating with multiple pages. We're actually gonna, in the service worker, end up using what's called a message channel to make these connections. So it ends up creating a unique set of ports that those communications can happen over.

And we're talking about local ports on the machines, since it's from page to service worker. So the message channel comes up with a unique set of ports for that communication to happen. Which means we need to send ours to that port, cuz the message channel figured that out, we didn't figure that out.

So the way we know what port is, is with the event object, if it has been specified, it's going to be evt.ports[0]
>> Kyle Simpson: So evt.ports[0] is going to be the target that we post a message to. It's not the service worker object, it's a specific message channel port that the service worker is listening on.

Let's do one other thing, which is that when we pass in this message, let's actually give it one wrapping object names. So we're gonna call this a statusUpdate, you asked for a status update, so let's give you a status update. That'll make it easier for us to determine in the service worker that this thing is, in fact, a status update.

Okay, we're sending the status update when requested. But there are other times that we should go ahead and proactively send that status update, so that the service worker stays in sync. So one of those times might be when there was a controller change. So we can say sendStatusUpdate, and we wanna target the service worker.

>> Kyle Simpson: For that message, we probably wanna do that right after we get the first reference to a service worker, just in case that we're able to get that information into it that quickly.
>> Kyle Simpson: But maybe for our purposes, the most important place for us to do that update is in the online and offline messages in here.

We're not passing in any targets, so it's just gonna automatically pick the currently active service workers in there.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now