ServiceWorker系列——ServiceWorker生命周期

本文深入解析ServiceWorker的生命周期,包括安装、激活、控制页面等关键阶段。探讨了ServiceWorker状态变化,如installing、installed、activating、activated及redundant的细节,以及各阶段的事件处理和回调函数的作用。

ServiceWorker生命周期

ServiceWorker本身是有状态的(installing,installed,activating,activated,redundant),这些状态构成了ServiceWorker生命周期:
clipboard.png

生命周期可细分为两个过程:

  1. 安装过程(installing,installed)
  2. 激活过程(activating, activated)

1. installing

当使用方法navigator.serviceWorker.register注册一个新的ServiceWorker时,浏览器会下载JS脚本,解析脚本。这时ServiceWorker处于installing状态。如果安装成功,则进入installed状态,否则会进入redundant状态。

1.1 InstallEvent对象

处于installing时会触发ServiceWorkerGlobalScope上下文的install事件。
index.html:

<!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 文件内容:

self.addEventListener('install', function(event) {
    console.log('install callback')
})
  1. 可通过registration.installing属性访问处于install状态的ServiceWorker;
  2. ServiceWorkerGlobalScope中可以绑定多个install事件回调函数,多个事件回调函数的参数是同一个InstallEvent对象;
  3. 如果install事件回调函数抛异常,则视为安装失败,ServiceWorker会进入redundant状态;
  4. 如果回调函数里调用installEvent.waitUntil(promise1)方法,则promise1被resolved时ServiceWorker对象才被installed,如果promise1被rejected则SW安装失败(redundant);
  5. 同一个事件回调函数或者多个事件回调函数可以多次调用installEvent.waitUntil(promise)方法,则表达所有的promise都被resolved时SW对象才被installed,只要存在promise被rejected则SW安装失败(redundant)。

修改sw.js内容,重新注册sw:

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)
    }))
})

观察控制台输出

clipboard.png

1.2 waitUntil方法

InstallEvent对象从父对象ExtendableEvent继承了waitUntil方法。该方法延长了事件对象的生存期。当传给waitUntil方法的Promise对象转成终态(resolved/rejected)才认为事件对象被处理完了。在事件对象被处理完之前ServiceWorker不会进入下一个状态(installed或者redundant)。

2. installed/waiting

ServiceWorker成功安装后便进入installed状态。至此Service完成了安装过程,等待进入激活过程

  1. 可通过registration.waiting属性访问处于installed状态的ServiceWorker(见上例);
  2. 如果页面没有激活的ServiceWorker管理,则ServiceWorker进入activating状态。

3. activating

ServiceWoker安装成功后进入activating状态。

3.1 ActivateEvent对象

  1. 处于activating时会触发ServiceWorkerGlobalScope上下文的activate事件。
  2. 可以绑定多个事件回调函数,多个事件回调ActivateEvent对象是同一个对象;
  3. 如果回调函数里调用activateEvent.waitUntil(promise1)方法,则表示promise1被resolved/rejected时SW对象才进入下一个状态activated
  4. 同一个事件回调函数或者多个事件回调函数可以多次调用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)
        }))
    })

    控制台输出:
    clipboard.png

  5. 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)
        }))
    })

    控制台输出:
    clipboard.png

也就是说激活过程中的任何错误不影响ServiceWoker被激活(脑洞下:新SW激活过程中说明页面已经没有被其他SW控制了,所以activate事件回调函数的执行失败并不会影响SW被激活。),注意这一点跟其他文档描述的可能不太一样。

4. activated

这时ServiceWorker可以控制页面了,可以监听功能事件了,如fetch,push事件。默认情况下ServiceWorker只能控制在其激活成功后才加载完成的页面。

4.1 fetch事件 & FetchEvent对象

ServiceWorker控制页面后就可以监听fetch事件了: 捕获请求,构建响应。注意:保证一个请求只有一个响应

  1. 可以绑定多个fetch事件,并且多个回调函数的fetchEvent是同一个对象;
  2. 如果回调函数内成功调用了fetchEvent.respondWith方法,则后面的回调函数不会被执行;
  3. 回调函数里多次调用fetchEvent.respondWith会报错的,即一个request只能有一个reponse
  4. 回调函数里最好是同步的方式调用fetchEvent.respondWith,异步调用不会阻止后面的后调函数执行, 很容易会造成多个reponse,导致错误3;
  5. 如果所有的回调函数里都没有调用fetchEvent.respondWith方法则会采用浏览器默认的fetch事件回调函数处理方式,即走网络请求;
  6. fetch回调函数还可以调用waitUntil方法,来延长FetchEvent事件对象的生命,如果有FetchEent对象还未处理完浏览器是不会自动关闭SW的。

5. redundant

redundant状态是ServiceWorker的终态。 关于serviceWorker如何变成redundant状态在Lavas Service Worker 生命周期the-service-worker-lifecycle参考中列举了3种,但是测试发现激活失败并不会导致哎,见上例。在这本书里Building Progressive Web Apps by Tal Ater的观点貌似论证了我们代码。这里再汇总下进入redundant的case:

  • register失败,如多次调用register,后调用注册的SW会变成redundant
  • 安装失败
  • 被新版本的ServiceWorker替换

再强调下激活过程不会导致ServiceWorker变成redundant状态。

参考

  1. Google 服务工作线程生命周期
  2. Chapter 4. Service Worker Lifecycle and Cache Management
  3. The Service Worker Lifecycle
  4. Lavas Service Worker 生命周期
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值