O ciclo de vida do service worker é a parte mais complicada dele. Se você não souber o que ele está tentando fazer e quais são os benefícios, pode parecer que ele está lutando contra você. Mas, depois de saber como ele funciona, você pode oferecer atualizações perfeitas e discretas aos usuários, misturando o melhor dos padrões da Web e nativos.
Este é um mergulho profundo, mas os marcadores no início de cada seção cobrem a maior parte do que você precisa saber.
A intent
A intenção do ciclo de vida é:
- Priorize o modo off-line.
- Permitir que um novo worker de serviço se prepare sem interromper o atual.
- Garanta que uma página no escopo seja controlada pelo mesmo service worker (ou nenhum) em todo o site.
- Verifique se há apenas uma versão do site em execução por vez.
A última é muito importante. Sem os service workers, os usuários podem carregar uma guia no seu site e abrir outra mais tarde. Isso pode fazer com que duas versões do site sejam executadas ao mesmo tempo. Às vezes, isso é aceitável, mas, se você estiver lidando com armazenamento, é possível que duas guias tenham opiniões muito diferentes sobre como o armazenamento compartilhado deve ser gerenciado. Isso pode resultar em erros ou, pior ainda, em perda de dados.
O primeiro service worker
Para resumir:
- O evento
install
é o primeiro evento que um worker de serviço recebe e acontece apenas uma vez. - Uma promessa transmitida para
installEvent.waitUntil()
indica a duração e o sucesso ou a falha da instalação. - Um worker de serviço não vai receber eventos como
fetch
epush
até que a instalação seja concluída e ele se torne "ativo". - Por padrão, as transferências de uma página não passam por um service worker, a menos que a própria solicitação da página tenha passado por um service worker. Portanto, você precisa atualizar a página para conferir os efeitos do service worker.
- O
clients.claim()
pode substituir esse padrão e assumir o controle de páginas não controladas.
Considere este HTML:
<!DOCTYPE html>
An image will appear here in 3 seconds:
<script>
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('SW registered!', reg))
.catch(err => console.log('Boo!', err));
setTimeout(() => {
const img = new Image();
img.src = '/dog.svg';
document.body.appendChild(img);
}, 3000);
</script>
Ele registra um worker de serviço e adiciona a imagem de um cachorro após três segundos.
Confira o service worker, sw.js
:
self.addEventListener('install', event => {
console.log('V1 installing…');
// cache a cat SVG
event.waitUntil(
caches.open('static-v1').then(cache => cache.add('/cat.svg'))
);
});
self.addEventListener('activate', event => {
console.log('V1 now ready to handle fetches!');
});
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
// serve the cat SVG from the cache if the request is
// same-origin and the path is '/dog.svg'
if (url.origin == location.origin && url.pathname == '/dog.svg') {
event.respondWith(caches.match('/cat.svg'));
}
});
Ele armazena em cache uma imagem de um gato e a exibe sempre que houver uma solicitação para /dog.svg
. No entanto, se você executar o exemplo acima, vai aparecer um cachorro na primeira vez que você carregar a página. Clique em "Atualizar" para ver o gato.
Escopo e controle
O escopo padrão de um registro de worker de serviço é ./
em relação ao URL do script. Isso significa que, se você registrar um worker de serviço em //example.com/foo/bar.js
, ele terá um escopo padrão de //example.com/foo/
.
Chamamos páginas, workers e workers compartilhados de clients
. Seu worker de serviço só pode controlar clientes que estão no escopo. Quando um cliente é "controlado", as buscas dele passam pelo worker de serviço no escopo. É possível detectar se um cliente é controlado por navigator.serviceWorker.controller
, que será nulo ou uma instância de service worker.
Fazer o download, analisar e executar
O primeiro service worker é feito o download quando você chama .register()
. Se o script falhar no download, na análise ou lançar um erro na execução inicial, a promessa de registro será rejeitada e o service worker será descartado.
As DevTools do Chrome mostram o erro no console e na seção do service worker da guia do aplicativo:

Instalar
O primeiro evento que um worker de serviço recebe é install
. Ele é acionado assim que o worker é executado e só é chamado uma vez por worker de serviço. Se você alterar o script do worker de serviço, o navegador vai considerá-lo como um worker de serviço diferente e ele vai receber o próprio evento install
. Vou falar sobre atualizações em detalhes mais tarde.
O evento install
é sua chance de armazenar em cache tudo o que você precisa antes de controlar os clientes. A promessa transmitida a event.waitUntil()
informa ao navegador quando a instalação é concluída e se ela foi bem-sucedida.
Se a promessa for rejeitada, isso sinaliza que a instalação falhou, e o navegador vai descartar o worker de serviço. Ele nunca vai controlar os clientes. Isso significa que podemos confiar na presença de cat.svg
no cache dos nossos eventos fetch
. É uma dependência.
Ativar
Quando o service worker estiver pronto para controlar clientes e processar eventos funcionais, como push
e sync
, você receberá um evento activate
. Mas isso não significa que a página que chamou .register()
será controlada.
Na primeira vez que você carrega a demonstração, mesmo que o dog.svg
seja solicitado muito tempo depois que o worker de serviço é ativado, ele não processa a solicitação, e você ainda vê a imagem do cachorro. O padrão é consistência. Se a página for carregada sem um service worker, os subrecursos também não serão carregados. Se você carregar a demonstração uma segunda vez (ou seja, atualizar a página), ela será controlada. A página e a imagem vão passar por eventos fetch
, e você vai ver um gato.
clients.claim
É possível assumir o controle de clientes não controlados chamando clients.claim()
no service worker depois que ele for ativado.
Confira uma variação da demonstração acima, que chama clients.claim()
no evento activate
. Você deve ver um gato na primeira vez. Eu digo "deve" porque isso é sensível ao tempo. Você só vai ver um gato se o worker de serviço for ativado e o clients.claim()
entrar em vigor antes que a imagem tente ser carregada.
Se você usar o service worker para carregar páginas de maneira diferente da que elas seriam carregadas pela rede, o clients.claim()
pode ser problemático, já que o service worker acaba controlando alguns clientes que foram carregados sem ele.
Como atualizar o worker de serviço
Para resumir:
- Uma atualização é acionada se ocorrer uma das seguintes situações:
- Uma navegação para uma página no escopo.
- Eventos funcionais, como
push
esync
, a menos que tenha havido uma verificação de atualização nas 24 horas anteriores. - Chamar
.register()
somente se o URL do service worker tiver mudado. No entanto, evite mudar o URL do worker.
- A maioria dos navegadores, incluindo o Chrome 68 e versões mais recentes, ignoram os cabeçalhos de armazenamento em cache por padrão ao verificar atualizações do script do worker de serviço registrado. Eles ainda respeitam os cabeçalhos de armazenamento em cache ao buscar recursos carregados em um service worker por
importScripts()
. Para modificar esse comportamento padrão, defina a opçãoupdateViaCache
ao registrar seu service worker. - Seu worker de serviço é considerado atualizado se for diferente em bytes do que o navegador já tem. Também estamos ampliando essa configuração para incluir scripts/módulos importados.
- O service worker atualizado é iniciado junto com o existente e recebe o próprio evento
install
. - Se o novo worker tiver um código de status não ok (por exemplo, 404), falhar na análise, gerar um erro durante a execução ou for rejeitado durante a instalação, o novo worker será descartado, mas o atual vai permanecer ativo.
- Depois de instalado, o worker atualizado vai
wait
até que o worker atual não esteja controlando nenhum cliente. Os clientes se sobrepõem durante uma atualização. self.skipWaiting()
evita a espera, ou seja, o worker de serviço é ativado assim que a instalação é concluída.
Digamos que mudamos o script do service worker para responder com uma imagem de um cavalo em vez de um gato:
const expectedCaches = ['static-v2'];
self.addEventListener('install', event => {
console.log('V2 installing…');
// cache a horse SVG into a new cache, static-v2
event.waitUntil(
caches.open('static-v2').then(cache => cache.add('/horse.svg'))
);
});
self.addEventListener('activate', event => {
// delete any caches that aren't in expectedCaches
// which will get rid of static-v1
event.waitUntil(
caches.keys().then(keys => Promise.all(
keys.map(key => {
if (!expectedCaches.includes(key)) {
return caches.delete(key);
}
})
)).then(() => {
console.log('V2 now ready to handle fetches!');
})
);
});
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
// serve the horse SVG from the cache if the request is
// same-origin and the path is '/dog.svg'
if (url.origin == location.origin && url.pathname == '/dog.svg') {
event.respondWith(caches.match('/horse.svg'));
}
});
Confira uma demonstração do acima. Você ainda vai ver a imagem de um gato. Por quê?
Instalar
Mudei o nome do cache de static-v1
para static-v2
. Isso significa que posso configurar o novo cache sem substituir as coisas no atual, que o antigo worker de serviço ainda está usando.
Esses padrões criam caches específicos da versão, semelhantes aos recursos que um app nativo agruparia com o executável. Você também pode ter caches que não são específicos da versão, como avatars
.
Aguardando
Depois de instalado, o service worker atualizado atrasa a ativação até que o service worker atual não controle mais os clientes. Esse estado é chamado de "aguardando" e é assim que o navegador garante que apenas uma versão do service worker esteja em execução por vez.
Se você executou a demonstração atualizada, ainda vai aparecer uma imagem de um gato, porque o worker V2 ainda não está ativado. O novo worker em espera aparece na guia "Application" das Ferramentas do desenvolvedor:

Mesmo que você tenha apenas uma guia aberta para a demonstração, atualizar a página não é suficiente para que a nova versão seja usada. Isso ocorre devido à maneira como as navegações do navegador funcionam. Quando você navega, a página atual não desaparece até que os cabeçalhos de resposta sejam recebidos. Mesmo assim, a página atual pode permanecer se a resposta tiver um cabeçalho Content-Disposition
. Por causa dessa sobreposição, o worker de serviço atual sempre controla um cliente durante uma atualização.
Para receber a atualização, feche ou saia de todas as guias usando o worker de serviço atual. Em seguida, quando você navegar até a demonstração novamente, o cavalo vai aparecer.
Esse padrão é semelhante ao do Chrome. As atualizações do Chrome são baixadas em segundo plano, mas não são aplicadas até que o Chrome seja reiniciado. Enquanto isso, você pode continuar usando a versão atual sem interrupções. No entanto, isso é uma dor de cabeça durante o desenvolvimento, mas as DevTools têm maneiras de facilitar isso, que serão abordadas mais adiante neste artigo.
Ativar
Isso é acionado quando o antigo service worker é removido e o novo pode controlar os clientes. Esse é o momento ideal para fazer coisas que você não podia fazer enquanto o worker antigo ainda estava em uso, como migrar bancos de dados e limpar caches.
Na demonstração acima, mantenho uma lista de caches que espero que estejam lá, e no evento activate
, me livro de todos os outros, o que remove o cache static-v1
antigo.
Se você transmitir uma promessa para event.waitUntil()
, ela vai armazenar eventos funcionais (fetch
, push
, sync
etc.) em buffer até que a promessa seja resolvida. Assim, quando o evento fetch
é acionado, a ativação é totalmente concluída.
Pular a fase de espera
A fase de espera significa que você está executando apenas uma versão do site por vez. No entanto, se você não precisar desse recurso, poderá ativar o novo worker de serviço mais cedo chamando self.skipWaiting()
.
Isso faz com que o worker do serviço expulse o worker ativo atual e se ative assim que entrar na fase de espera (ou imediatamente, se já estiver nessa fase). Isso não faz com que o worker pule a instalação, apenas espera.
Não importa quando você chamar skipWaiting()
, desde que seja durante ou antes da espera. É bastante comum chamar esse método no evento install
:
self.addEventListener('install', event => {
self.skipWaiting();
event.waitUntil(
// caching etc
);
});
Mas talvez você queira chamar como resultado de um postMessage()
para o worker de serviço. Ou seja, você quer skipWaiting()
após uma interação do usuário.
Confira uma demonstração que usa skipWaiting()
. Você vai ver a imagem de uma vaca sem precisar sair da página. Assim como o clients.claim()
, é uma corrida, então você só vai ver a vaca se o novo worker de serviço buscar, instalar e ativar antes que a página tente carregar a imagem.
Atualizações manuais
Como mencionei anteriormente, o navegador verifica atualizações automaticamente após navegações e eventos funcionais, mas você também pode acionar manualmente:
navigator.serviceWorker.register('/sw.js').then(reg => {
// sometime later…
reg.update();
});
Se você espera que o usuário use seu site por um longo período sem recarregá-lo, chame update()
em um intervalo (por exemplo, a cada hora).
Evite mudar o URL do script do service worker
Se você leu minha postagem sobre práticas recomendadas de armazenamento em cache, considere atribuir um URL exclusivo a cada versão do worker de serviço. Não faça isso. Essa geralmente é uma prática ruim para service workers. Atualize o script no local atual.
Isso pode causar um problema como este:
index.html
registrasw-v1.js
como um worker de serviço.- O
sw-v1.js
armazena em cache e serveindex.html
para que ele funcione off-line. - Você atualiza
index.html
para registrar o novo e brilhantesw-v2.js
.
Se você fizer isso, o usuário nunca receberá sw-v2.js
, porque sw-v1.js
está exibindo a versão antiga de index.html
do cache. Você se colocou em uma posição em que precisa atualizar o worker de serviço para atualizar o worker de serviço. Eca.
No entanto, para a demonstração acima, mudei o URL do service worker. Para fins de demonstração, você pode alternar entre as versões. Não é algo que eu faria na produção.
Facilitar o desenvolvimento
O ciclo de vida do service worker é criado pensando no usuário, mas durante o desenvolvimento, é um pouco difícil. Felizmente, há algumas ferramentas que podem ajudar:
Atualizar ao recarregar
Essa é a minha favorita.

Isso muda o ciclo de vida para ser mais fácil para os desenvolvedores. Cada navegação vai:
- Recarregue o worker de serviço.
- Instale como uma nova versão, mesmo que seja idêntica ao byte, ou seja, seu evento
install
é executado e seus caches são atualizados. - Pular a fase de espera para que o novo worker de serviço seja ativado.
- Navegue pela página.
Isso significa que você vai receber as atualizações em cada navegação (incluindo a atualização) sem precisar recarregar duas vezes ou fechar a guia.
Pular a espera

Se você tiver um worker em espera, clique em "skip waiting" no DevTools para promover imediatamente o worker para "active".
Shift-reload
Se você atualizar a página à força (com a tecla Shift pressionada), o service worker será ignorado. Ela será descontrolada. Esse recurso está na especificação e funciona em outros navegadores que oferecem suporte a service workers.
Como processar atualizações
O worker de serviço foi projetado como parte da Web extensível. A ideia é que nós, como desenvolvedores de navegadores, reconhecemos que não somos melhores em desenvolvimento da Web do que os desenvolvedores da Web. Por isso, não devemos fornecer APIs de alto nível restritas que resolvam um problema específico usando padrões que gostamos. Em vez disso, vamos dar acesso ao núcleo do navegador e permitir que você faça o que quiser, da maneira que funcionar melhor para seus usuários.
Para ativar o maior número possível de padrões, todo o ciclo de atualização é observável:
navigator.serviceWorker.register('/sw.js').then(reg => {
reg.installing; // the installing worker, or undefined
reg.waiting; // the waiting worker, or undefined
reg.active; // the active worker, or undefined
reg.addEventListener('updatefound', () => {
// A wild service worker has appeared in reg.installing!
const newWorker = reg.installing;
newWorker.state;
// "installing" - the install event has fired, but not yet complete
// "installed" - install complete
// "activating" - the activate event has fired, but not yet complete
// "activated" - fully active
// "redundant" - discarded. Either failed install, or it's been
// replaced by a newer version
newWorker.addEventListener('statechange', () => {
// newWorker.state has changed
});
});
});
navigator.serviceWorker.addEventListener('controllerchange', () => {
// This fires when the service worker controlling this page
// changes, eg a new worker has skipped waiting and become
// the new active worker.
});
O ciclo de vida continua
Como você pode ver, vale a pena entender o ciclo de vida do service worker. Com esse entendimento, os comportamentos do service worker vão parecer mais lógicos e menos misteriosos. Esse conhecimento vai dar mais confiança ao implantar e atualizar os service workers.