Service worker communicate to clients
Asked Answered
B

2

25

I have been trying to send messages to clients from service worker but if I use

self.clients.matchAll()
.then((clients) => {
  clients.forEach(function(client) {
    client.postMessage({msg: 'Hello from SW'})
  })
})

it won't send to any client even if I have a tab open in the browser, but if I send a message from the client to the service worker

// client
navigator.serviceWorker.controller.postMessage({title: 'Send message from client'})

and in the service worker

self.addEventListener('message', function(event) {
  self.clients.fetchAll()
    .then((clients) => {
      clients.forEach(function(client) {
        client.postMessage({msg: 'Hello from SW'})
     })
  })
})

it can send a message and finds clients. what am I doing wrong?, should I use indexedDB instead?

Barringer answered 9/2, 2017 at 2:22 Comment(0)
B
71

I don't believe that clients.fetchAll() exists. You probably mean clients.matchAll(), which should give you the behavior you describe:

self.clients.matchAll().then(clients => {
    clients.forEach(client => client.postMessage({msg: 'Hello from SW'}));
})

A nicer alternative allowing the service worker to communicate with clients, while avoiding having to send messages one-by-one, is to make use of the Broadcast Channel API. It is now supported in recent versions of Chrome as well as in Firefox.

// From service-worker.js:
const channel = new BroadcastChannel('sw-messages');
channel.postMessage({title: 'Hello from SW'});

// From your client pages:
const channel = new BroadcastChannel('sw-messages');
channel.addEventListener('message', event => {
    console.log('Received', event.data);
});
Burtie answered 10/2, 2017 at 15:26 Comment(7)
You are right about the self.clients.matchAll, I have used that, the API of the Broadcast Channel works really nice. Thanks.Barringer
thanks man BroadcastChannel worked for me, but when we are doing client.postMessage we should be able to catch response in page using below code right?navigator.serviceWorker.addEventListener('message', function(event) { console.log('Received a message from service worker: ', event.data); }); why isnt this code working?Whereto
@Whereto Were you able to find an answer for the question in your comment?Canard
I used that as well. But I was just wondering if you ever got client.postMessage workingCanard
BroadcastChannel does not work in Safari :( I've got v14Voss
client.postMessage works for me. Agree with @t1gor, broadcastChannel has worse browser compatibility compared to SW. postMessage should be the API to use if you care about Safari.Arthur
From the API doc, seems it should be this: channel.onmessage = function(e) { console.log('Received', e.data); };Cockloft
T
6

Adding includeUncontrolled option should do:

for (const client of await clients.matchAll({includeUncontrolled: true, type: 'window'})) {
  client.postMessage(message);
}
Testicle answered 8/11, 2019 at 11:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.