Chrome ServiceWorker postMessage

user24502 picture user24502 · May 11, 2015 · Viewed 14.1k times · Source

I try to postMessage between a web application and the corresponding service worker. The service worker is successfully registered and working so far. Unfortunately, I noticed some strange behavior:

  1. The navigator.serviceWorker.controller is always null.
  2. At the service worker side, I implemented postMessage as follows:
self.addEventListener('message', function (evt) {
    console.log('postMessage received', evt);
});

Unfortunately, the important fields to post back to the origin evt.origin='' and evt.source=null do not contain the desired values. Nonetheless, I always received the sent evt.data.

Do you know how to post back?

Thank you very much!
Andi

Answer

Manu picture Manu · May 14, 2015

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.