问题
I try to postMessage between a WebApp and the corresponding ServiceWorker. The serviceWoker is successfully registered and working so far.
Unfortunately I noticed some strange behavior:
1. The navigator.serviceWorker.controller is always null.
2. At the serviceWorker side I implemented postMessage this way:
self.addEventListener('message', function (evt) {
console.log('postMessage received', evt);
});
Unfortunately the important field to post back to origin evt.origin=““ and evt.source=null do not contain the desired values. But I always receive the sent evt.data.
Do you know how to post back?
Thank you very much!
Andi
回答1:
One way to send a response from the worker back to the controlled app is shown in this demo and is done the following way (I haven't actually tested this yet, but the demo works well and the code seems to agree with the specs).
In the main page:
function sendMessage(message) {
// This wraps the message posting/response in a promise, which will
// resolve if the response doesn't contain an error, and reject with
// the error if it does. If you'd prefer, it's possible to call
// controller.postMessage() and set up the onmessage handler
// independently of a promise, but this is a convenient wrapper.
return new Promise(function(resolve, reject) {
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = function(event) {
if (event.data.error) {
reject(event.data.error);
} else {
resolve(event.data);
}
};
// This sends the message data as well as transferring
// messageChannel.port2 to the service worker.
// The service worker can then use the transferred port to reply
// via postMessage(), which will in turn trigger the onmessage
// handler on messageChannel.port1.
// See
// https://html.spec.whatwg.org/multipage/workers.html#dom-worker-postmessage
navigator.serviceWorker.controller.postMessage(message, [messageChannel.port2]);
});
}
And in the service worker code:
self.addEventListener('message', function(event) {
event.ports[0].postMessage({'test': 'This is my response.'});
});
You should then be able to use the promise returned by the sendMessage
function to do what you want with the response coming from the service worker.
回答2:
@GalBracha was asking if you could communicate with the client without first sending a message - yes!. Here's a quick example of how I did it when I wanted to send a message to clients when a push notification was received (not when the user clicked on the notification, but when the service worker received the event):
in your client js (after service worker registration, etc):
// navigator.serviceWorker.addEventListener('message', ... ) should work too
navigator.serviceWorker.onmessage = function (e) {
// messages from service worker.
console.log('e.data', e.data);
};
in your service worker, in response to some event (perhaps the install event):
// find the client(s) you want to send messages to:
self.clients.matchAll(/* search options */).then( (clients) => {
if (clients && clients.length) {
// you need to decide which clients you want to send the message to..
const client = clients[0];
client.postMessage("your message");
}
the trick is (obviously?) to attach your listener for the message event to the serviceWorker object, not the window object.
来源:https://stackoverflow.com/questions/30177782/chrome-serviceworker-postmessage