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
状态。