2016-08-08 9 views
15

(jestem parafrazując pytanie zadane przez Rich Harris w „Stuff I wish I'd known sooner about service workers” GIST).Kiedy działa kod w obiekcie serwisowym poza programem obsługi zdarzeń?

Jeśli mam kod w moim pracownika serwisu, który działa poza programu obsługi zdarzeń, kiedy to uruchomić?

I, co jest ściśle związane z tym, jaka jest różnica między umieszczeniem w rękojeści install a całkowitym umieszczeniem jej poza trenerem zdarzeń?

Odpowiedz

28

Ogólnie kod znajdujący się poza jakimkolwiek programem obsługi zdarzeń, w "najwyższym poziomie" globalnego zasięgu pracownika obsługi serwisowej, będzie uruchamiany za każdym razem, gdy wątek (proces) pracownika serwisowego zostanie uruchomiony. Wątek serwisanta może się uruchamiać (i zatrzymywać) w dowolnym momencie i nie jest związany z czasem życia kontrolowanych przez niego stron internetowych.

(Uruchamianie/zatrzymywanie wątku pracownika serwisowego często jest optymalizacją wydajności/baterii i zapewnia, że ​​np. Tylko dlatego, że przeglądasz stronę, która zarejestrowała pracownika serwisu, nie uzyskasz dodatkowego wirowania jałowego wątku w tle.)

Odwrotną stroną tego jest to, że za każdym razem, gdy wątek usługi serwisowej jest zatrzymany, każdy istniejący stan globalny zostaje zniszczony. Tak więc, podczas gdy możesz dokonać pewnych optymalizacji, takich jak przechowywanie otwartego połączenia IndexedDB w stanie globalnym w nadziei na udostępnienie go w wielu zdarzeniach, musisz być przygotowany na ich ponowną inicjalizację, jeśli wątek został zabity pomiędzy wywołaniami obsługi obsługi zdarzeń.

Ściśle związane z tym pytaniem jest nieporozumienie, które widziałem na temat obsługi zdarzeń install. Widziałem, jak niektórzy programiści używają procedury obsługi install do inicjalizacji stanu globalnego, z którego następnie korzystają w innych programach obsługi zdarzeń, takich jak fetch. Jest to niebezpieczne i prawdopodobnie doprowadzi do błędów w produkcji. Procedura obsługi install jest uruchamiana raz dla każdej wersji pracownika serwisowego i zwykle najlepiej jest używać go do zadań związanych z obsługą wersji roboczych, takich jak buforowanie nowych lub zaktualizowanych zasobów wymaganych przez tę wersję. Po pomyślnym zakończeniu obsługi install dana wersja pracownika serwisowego zostanie uznana za "zainstalowaną", a program obsługi install nie będzie ponownie uruchamiany, gdy pracownik serwisu rozpocznie obsługę np. Zdarzenia fetch lub message.

Tak więc, jeśli istnieje stan globalny, który musi zostać zainicjowany przed rozpoczęciem obsługi, np. Zdarzenie fetch, można to zrobić w globalnym zasięgu pracownika serwisowego najwyższego poziomu (opcjonalnie czekając na obietnicę rozstrzygnięcia wewnątrz fetch obsługa zdarzeń, aby upewnić się, że wszystkie operacje asynchroniczne zostały zakończone). Do nie polegać na obsłudze install, aby ustawić zasięg globalny!

Oto przykład, który ilustruje niektóre z tych punktów:

// Assume this code lives in service-worker.js 

// This is top-level code, outside of an event handler. 
// You can use it to manage global state. 

// _db will cache an open IndexedDB connection. 
let _db; 
const dbPromise =() => { 
    if (_db) { 
    return Promise.resolve(_db); 
    } 

    // Assume we're using some Promise-friendly IndexedDB wrapper. 
    // E.g., https://www.npmjs.com/package/idb 
    return idb.open('my-db', 1, upgradeDB => { 
    return upgradeDB.createObjectStore('key-val'); 
    }).then(db => { 
    _db = db; 
    return db; 
    }); 
}; 

self.addEventListener('install', event => { 
    // `install` is fired once per version of service-worker.js. 
    // Do **not** use it to manage global state! 
    // You can use it to, e.g., cache resources using the Cache Storage API. 
}); 

self.addEventListener('fetch', event => { 
    event.respondWith(
    // Wait on dbPromise to resolve. If _db is already set, because the 
    // service worker hasn't been killed in between event handlers, the promise 
    // will resolve right away and the open connection will be reused. 
    // Otherwise, if the global state was reset, then a new IndexedDB 
    // connection will be opened. 
    dbPromise().then(db => { 
     // Do something with IndexedDB, and eventually return a `Response`. 
    }); 
); 
}); 
+1

Jest to bardzo pomocne, dziękuję! –