Passing state info into a service worker before `install`

前端 未结 2 1279
离开以前
离开以前 2020-12-13 14:45

Background

I\'m new to service workers but working on a library that is intended to become \"offline-first\" (really, almost \"offline-only\") (FWIW

2条回答
  •  北海茫月
    2020-12-13 15:32

    Update 3:

    And since it is not supposed to be safe to rely on globals within the worker, my messaging solution seems even less sound. I think it either has to be Jeff Posnick's solution (in some cases, importScripts may work).

    Update 2:

    Although not directly related to the topic of this thread relating to "install" event, as per a discussion starting at https://github.com/w3c/ServiceWorker/issues/659#issuecomment-384919053 , there are some issues, particularly with using this message-passing approach for the activate event. Namely, the activate event may never fail, and thus never be tried again, leaving one's application in an unstable state. (A failure of install will at least not apply the new service worker to old pages, whereas activate will keep fetches on hold until the event completes, which it may never do if it is left waiting for a message that was not received, and which anything but a new worker will fail to correct since new pages won't be able to load to send that message again.)

    Update:

    Although I got the client from within the install script in Chrome, I wasn't able to receive the message back with navigator.serviceWorker.onmessage for some reason.

    However, I was able to fully confirm the following approach in its place:

    In the service worker:

    self.addEventListener('install', e => {
        e.waitUntil(
            new Promise((resolve, reject) => {
                self.addEventListener('message', ({data: {
                    myData
                }}) => {
                    // Do something with `myData` here
                    //    then when ready, `resolve`
                });
            })
        );
     });
    

    In the calling script:

    navigator.serviceWorker.register('sw.js').then((r) => {
        r.installing.postMessage({myData: 100});
    });
    

    @JeffPosnick 's is the best answer for the simple case I described in the OP, but I thought I'd present my discovering that one can get messages from and into a service worker script early (tested on Chrome) by such as the following:

    In the service worker:

    self.addEventListener('install', e => {
        e.waitUntil(self.clients.matchAll({
            includeUncontrolled: true,
            type: 'window'
        }).then((clients) => new Promise((resolve, reject) => {
            if (clients && clients.length) {
                const client = clients.pop();
                client.postMessage('send msg to main script');
                // One should presumably be able to poll to check for a
                //   variable set in the SW message listener below
                //   and then `resolve` when set
                // Despite the unreliability of setting globals in SW's
                //   I believe this could be safe here as the `install`
                //   event is to run while the main script is still open.
            }
        })));
    });
    
    self.addEventListener('message', e => {
        console.log('SW receiving main script msg', e.data);
        e.ports[0].postMessage('sw response');
    });
    

    In the calling script:

    navigator.serviceWorker.addEventListener('message', (e) => {
        console.log('msg recd in main script', e.data);
        e.source.postMessage('sending back to sw');
    });
    return navigator.serviceWorker.register(
        'sw.js'
    ).then((r) => {
        // navigator.serviceWorker.ready.then((r) => { // This had been necessary at some point in my testing (with r.active.postMessage), but not working for me atm...
            // Sending a subsequent message
            const messageChannel = new MessageChannel();
            messageChannel.port1.onmessage = (e) => {
                if (e.data.error) {
                    console.log('err', e.data.error);
                } else {
                    console.log('data', e.data);
                }
            };
            navigator.serviceWorker.controller.postMessage('sending to sw', [messageChannel.port2]);
        // });
    });
    

提交回复
热议问题