ServiceWorker自己是有狀態的(installing,installed,activating,activated,redundant),這些狀態構成了ServiceWorker生命週期:javascript
生命週期可細分爲兩個過程:html
當使用方法navigator.serviceWorker.register
註冊一個新的ServiceWorker時,瀏覽器會下載JS腳本,解析腳本。這時ServiceWorker處於installing
狀態。若是安裝成功,則進入installed
狀態,不然會進入redundant
狀態。java
處於installing時會觸發ServiceWorkerGlobalScope上下文的install
事件。
index.html:git
<!DOCTYPE html> <html> <head> <title>Service Life Cycle</title> <meta name="viewport" content="width=device-width,initial-scale=1.0"> </head> <body> <script> // Check that service workers are registered if ('serviceWorker' in navigator) { // Use the window load event to keep the page load performant window.addEventListener('load', () => { navigator.serviceWorker.register('sw-lifecyle.js?v=' + 1).then((registration) => { var sw = null, state; if(registration.installing) { sw = registration.installing; state = 'installing'; } else if(registration.waiting) { sw = registration.waiting; state = 'installed' } else if(registration.active) { sw = registration.active; state = 'activated' } state && console.log(`sw state is ${state}`); if(sw) { sw.onstatechange = function() { console.log(`sw state is ${sw.state}`); } } }); }); } </script> </body> </html>
sw.js 文件內容:github
self.addEventListener('install', function(event) { console.log('install callback') })
registration.installing
屬性訪問處於install
狀態的ServiceWorker;ServiceWorkerGlobalScope
中能夠綁定多個install
事件回調函數,多個事件回調函數的參數是同一個InstallEvent對象;redundant
狀態;installEvent.waitUntil(promise1)
方法,則promise1被resolved時ServiceWorker對象才被installed,若是promise1被rejected則SW安裝失敗(redundant);installEvent.waitUntil(promise)
方法,則表達全部的promise都被resolved時SW對象才被installed,只要存在promise被rejected則SW安裝失敗(redundant)。修改sw.js內容,從新註冊sw:web
self.addEventListener('install', function(event) { event._from = 'callback1' console.log('install callback 1: ' + (Date.now()/1000)) var i = 0; while(i < 1000000) { ++i; } // 屢次調用 event.waitUntil(new Promise(function(resolve) { setTimeout(() => { console.log('resolve 2s') resolve() }, 2000) })) event.waitUntil(new Promise(function(resolve, reject) { setTimeout(() => { console.log('resolve 3s') resolve() // }, 3000) })) }) // 屢次綁定Install事件 self.addEventListener('install', function(event) { event._from = 'callback2' console.log('install callback 2: ' + (Date.now()/1000)) event.waitUntil(new Promise(function(resolve) { setTimeout(() => { console.log('resolve 5s') resolve() }, 5000) })) })
觀察控制檯輸出promise
InstallEvent
對象從父對象ExtendableEvent
繼承了waitUntil方法。該方法延長了事件對象的生存期。當傳給waitUntil
方法的Promise對象轉成終態(resolved/rejected)才認爲事件對象被處理完了。在事件對象被處理完以前ServiceWorker不會進入下一個狀態(installed或者redundant)。瀏覽器
ServiceWorker成功安裝後便進入installed
狀態。至此Service完成了安裝過程
,等待進入激活過程
。網絡
registration.waiting
屬性訪問處於installed
狀態的ServiceWorker(見上例);activating
狀態。ServiceWoker安裝成功後進入activating
狀態。異步
activating
時會觸發ServiceWorkerGlobalScope上下文的activate
事件。activateEvent.waitUntil(promise1)
方法,則表示promise1被resolved/rejected時SW對象才進入下一個狀態activated
;同一個事件回調函數或者多個事件回調函數能夠屢次調用activateEvent.waitUntil(promise)方法,則表達全部的promise都被resolved/rejected時SW對象才進入下一個狀態activated
;
修改sw.js文件:
self.addEventListener('activate', function(event) { event._from = 'callback1' console.log('activate callback 1: ' + (Date.now()/1000)) var i = 0; while(i < 1000000) { ++i; } event.waitUntil(new Promise(function(resolve, reject) { setTimeout(() => { console.log('resolve 2s') resolve() }, 2000) })) event.waitUntil(new Promise(function(resolve, reject) { setTimeout(() => { console.log('resolve 4s') resolve() }, 4000) })) }) self.addEventListener('activate', (event) => { event.waitUntil(new Promise((resolve, reject) =>{ setTimeout(() => { resolve('resolve activate') }, 5000) })) })
控制檯輸出:
跟install
事件回調函數不同的是在activate
事件回調函數裏拋異常,或者傳給activateEvent.waitUntil(promise1)方法
的promise被reject都不會影響ServiceWorker進入activated狀態。
再次修改sw.js:
self.addEventListener('activate', function(event) { event._from = 'callback1' console.log('activate callback 1: ' + (Date.now()/1000)) var i = 0; while(i < 1000000) { ++i; } event.waitUntil(new Promise(function(resolve, reject) { setTimeout(() => { console.log('resolve 2s') resolve() }, 2000) })) event.waitUntil(new Promise(function(resolve, reject) { setTimeout(() => { console.log('resolve 4s') resolve() }, 4000) })) }) self.addEventListener('activate', (event) => { throw 'error' event.waitUntil(new Promise((resolve, reject) =>{ setTimeout(() => { resolve('reject activate') }, 1) })) }) self.addEventListener('activate', (event) => { event.waitUntil(new Promise((resolve, reject) =>{ setTimeout(() => { reject('reject activate') }, 5000) })) })
控制檯輸出:
也就是說激活過程當中的任何錯誤不影響ServiceWoker被激活(腦洞下:新SW激活過程當中說明頁面已經沒有被其餘SW控制了,因此activate事件回調函數的執行失敗並不會影響SW被激活。),注意這一點跟其餘文檔描述的可能不太同樣。
這時ServiceWorker能夠控制頁面了,能夠監聽功能事件了,如fetch,push事件。默認狀況下ServiceWorker只能控制在其激活成功後才加載完成的頁面。
ServiceWorker控制頁面後就能夠監聽fetch事件了: 捕獲請求,構建響應。注意:保證一個請求只有一個響應
fetchEvent.respondWith
方法,則後面的回調函數不會被執行;fetchEvent.respondWith
會報錯的,即一個request只能有一個reponse;fetchEvent.respondWith
,異步調用不會阻止後面的後調函數執行, 很容易會形成多個reponse,致使錯誤3;fetchEvent.respondWith
方法則會採用瀏覽器默認的fetch事件回調函數處理方式,即走網絡請求;waitUntil
方法,來延長FetchEvent事件對象的生命,若是有FetchEent對象還未處理完瀏覽器是不會自動關閉SW的。redundant
狀態是ServiceWorker的終態。 關於serviceWorker如何變成redundant
狀態在Lavas Service Worker 生命週期和 the-service-worker-lifecycle參考中列舉了3種,可是測試發現激活失敗並不會致使哎,見上例。在這本書裏Building Progressive Web Apps by Tal Ater的觀點貌似論證了咱們代碼。這裏再彙總下進入redundant的case:
再強調下激活過程不會致使ServiceWorker變成redundant
狀態。