Aprenda o que são Web Workers e Service Workers, como diferem e quando usar cada um para páginas mais rápidas, tarefas em segundo plano, cache e suporte offline.

Os navegadores executam a maior parte do seu JavaScript na thread principal — o mesmo lugar que lida com entrada do usuário, animações e a pintura da página. Quando trabalho pesado acontece ali (parsing de grandes volumes de dados, processamento de imagens, cálculos complexos), a interface pode travar ou “congelar”. Os workers existem para mover certas tarefas para fora da thread principal ou fora do controle direto da página, para que sua app continue responsiva.
Se sua página está ocupada fazendo uma computação de 200ms, o navegador não consegue rolar suavemente, responder a cliques ou manter animações a 60fps. Workers ajudam permitindo que você execute trabalho em segundo plano enquanto a thread principal foca na interface.
Um Web Worker é uma thread JavaScript em segundo plano que você cria a partir de uma página. É ideal para tarefas pesadas de CPU que, de outra forma, bloqueariam a UI.
Um Service Worker é um tipo especial de worker que fica entre sua web app e a rede. Ele pode interceptar requisições, armazenar respostas em cache e habilitar recursos como suporte offline e notificações push.
Pense em um Web Worker como um ajudante fazendo cálculos em outra sala. Você envia uma mensagem, ele processa e responde.
Pense em um Service Worker como um guarda na porta. Requisições por páginas, scripts e chamadas de API passam por ele, e ele pode decidir buscar da rede, servir do cache ou responder de forma personalizada.
Ao final, você saberá:
postMessage) se encaixa no modelo de workers, e por que a Cache Storage API importa para o modo offlineEsta visão estabelece o “porquê” e o modelo mental — a seguir vamos nos aprofundar em como cada tipo de worker se comporta e onde se encaixa em projetos reais.
Quando você abre uma página web, a maior parte do que você “sente” acontece na thread principal. Ela é responsável por desenhar pixels (renderização), reagir a toques e cliques (entrada) e executar muito do JavaScript.
Porque renderização, tratamento de entrada e JavaScript frequentemente revezam na mesma thread, uma tarefa lenta pode fazer todo o resto esperar. É por isso que problemas de performance tendem a se manifestar como problemas de responsividade, não apenas “código lento”.
Como o bloqueio se apresenta para os usuários:
JavaScript tem muitas APIs assíncronas — fetch(), timers, eventos — que ajudam a evitar espera ativa. Mas assíncrono não faz o trabalho pesado rodar ao mesmo tempo que a renderização.
Se você fizer computação cara (processamento de imagem, parsing de JSON grande, criptografia, filtragem complexa) na thread principal, ela ainda competirá com as atualizações de UI. “Assíncrono” pode adiar quando algo roda, mas pode continuar executando na mesma thread principal e causar jank quando ocorrer.
Workers existem para que os navegadores mantenham a página responsiva enquanto ainda fazem trabalho significativo.
Em suma: workers protegem a thread principal para que sua app continue interativa enquanto realiza trabalho real em segundo plano.
Um Web Worker é uma forma de executar JavaScript fora da thread principal. Em vez de competir com o trabalho de UI (renderização, rolagem, resposta a cliques), um worker roda em sua própria thread de fundo para que tarefas pesadas terminem sem fazer a página parecer “presa”.
Pense nele como: a página permanece focada na interação do usuário, enquanto o worker trata tarefas pesadas de CPU como parsing de arquivos grandes, cálculos ou preparação de dados para gráficos.
Um Web Worker roda em uma thread separada com seu próprio escopo global. Ele ainda tem acesso a muitas APIs web (timers, fetch em vários navegadores, crypto, etc.), mas é propositalmente isolado da página.
Existem algumas variantes comuns:
Se você nunca usou workers antes, a maioria dos exemplos mostra Dedicated Workers.
Workers não chamam funções da sua página diretamente. A comunicação acontece por mensagens:
postMessage().postMessage().Para dados binários grandes, você pode melhorar a performance transferindo a propriedade de um ArrayBuffer (assim ele não é copiado), mantendo a passagem de mensagens rápida.
Porque um worker é isolado, há algumas restrições importantes:
window ou document. Workers rodam sob self (um escopo global de worker), e as APIs disponíveis podem diferir da página principal.Usado corretamente, um Web Worker é uma das maneiras mais simples de melhorar o desempenho da thread principal sem mudar o que sua app faz — apenas onde o trabalho caro acontece.
Web Workers são ótimos quando sua página fica “presa” porque o JavaScript está fazendo trabalho demais na thread principal. A thread principal também é responsável por interações do usuário e renderização, então tarefas pesadas ali podem causar jank, cliques atrasados e rolagem congelada.
Use um Web Worker quando você tem trabalho pesado de CPU que não precisa de acesso direto ao DOM:
Um exemplo prático: se você recebe um payload JSON grande e o parsing causa stutter na UI, mova o parsing para um worker e envie o resultado de volta.
A comunicação com um worker acontece via postMessage. Para dados binários grandes, prefira objetos transferíveis (como ArrayBuffer) para que o navegador transfira a propriedade de memória ao worker em vez de copiá-la.
// main thread
worker.postMessage(buffer, [buffer]); // transfere o ArrayBuffer
Isso é especialmente útil para buffers de áudio, bytes de imagem ou outros grandes blocos de dados.
Workers têm overhead: arquivos extras, passagem de mensagens e fluxo de depuração diferente. Evite-os quando:
postMessage pode anular o benefício.Se uma tarefa pode causar uma pausa perceptível (frequentemente ~50ms+) e pode ser expressa como “entrada → computação → saída” sem acesso ao DOM, um Web Worker geralmente vale a pena. Se for principalmente atualizações de UI, mantenha na thread principal e otimize lá.
Um Service Worker é um arquivo JavaScript especial que roda em segundo plano no navegador e atua como uma camada de rede programável para seu site. Em vez de rodar na própria página, ele fica entre sua web app e a rede, permitindo decidir o que acontece quando a app solicita recursos (HTML, CSS, chamadas de API, imagens).
Um Service Worker tem um ciclo de vida separado de qualquer aba:
Como ele pode ser parado e reiniciado a qualquer momento, trate-o como um script orientado a eventos: faça trabalho rápido, armazene estado em armazenamento persistente e evite supor que ele está sempre em execução.
Service Workers são limitados à mesma origem (mesmo domínio/protocolo/porta) e controlam apenas páginas sob seu escopo — normalmente a pasta onde o arquivo do worker é servido (e abaixo dela). Eles também exigem HTTPS (exceto localhost) porque podem afetar requisições de rede.
Um Service Worker é usado principalmente para ficar entre sua web app e a rede. Ele pode decidir quando usar a rede, quando usar dados em cache e quando fazer algum trabalho em segundo plano — sem bloquear a página.
A tarefa mais comum é habilitar experiências offline ou em conexões ruins, fazendo cache de assets e respostas.
Algumas estratégias práticas de cache:
Geralmente isso é implementado com a Cache Storage API e manipulação do evento fetch.
Service Workers podem melhorar a percepção de velocidade em visitas de retorno por:
O resultado é menos requisições de rede, inicialização mais rápida e performance mais consistente em conexões instáveis.
Service Workers podem alimentar funcionalidades de background como push notifications e background sync (suporte varia por navegador/plataforma). Isso significa que você pode notificar usuários ou reenviar uma requisição falhada mais tarde — mesmo que a página não esteja aberta.
Se você está construindo um progressive web app, Service Workers são uma peça central por trás de:
Se lembrar de apenas uma coisa: Web Workers ajudam sua página a fazer trabalho pesado sem congelar a UI, enquanto Service Workers ajudam sua app a controlar requisições de rede e se comportar como um app instalável (PWA).
Um Web Worker é para tarefas pesadas de CPU — parsing de grandes dados, gerar thumbnails, crunching de números — para que a thread principal permaneça responsiva.
Um Service Worker é para manuseio de requisições e tarefas do ciclo de vida da app — suporte offline, estratégias de cache, background sync e push notifications. Ele pode ficar entre sua app e a rede.
Um Web Worker normalmente está vinculado à página/aba. Quando a página some, o worker geralmente também some (a menos que você use casos especiais como SharedWorker).
Um Service Worker é orientado a eventos. O navegador pode iniciá-lo para lidar com um evento (como fetch ou push) e encerrá-lo quando ocioso. Isso significa que ele pode rodar mesmo quando nenhuma aba está aberta, desde que um evento o desperte.
Um Web Worker não pode interceptar requisições da página. Ele pode usar fetch() para buscar dados, mas não pode reescrever, cachear ou servir respostas para outras partes do seu site.
Um Service Worker pode interceptar requisições de rede (via evento fetch), decidir se vai à rede, responder do cache ou retornar um fallback.
Um Web Worker não gerencia o cache HTTP da sua app.
Um Service Worker usa comumente a Cache Storage API para armazenar e servir pares requisição/resposta — isso é a base para cache offline e carregamentos “instantâneos” em visitas repetidas.
Colocar um worker para rodar é principalmente sobre onde ele roda e como é carregado. Web Workers são criados diretamente por um script de página. Service Workers são registrados por uma página que o navegador então instala e que fica “à frente” das requisições de rede do seu site.
Um Web Worker nasce quando sua página cria um. Você aponta para um arquivo JavaScript separado e se comunica via postMessage.
// main.js (running on the page)
const worker = new Worker('/workers/resize-worker.js', { type: 'module' });
worker.postMessage({ action: 'start', payload: { /* ... */ } });
worker.onmessage = {
.(, event.);
};
Um bom modelo mental: o arquivo do worker é apenas outra URL de script que sua página pode buscar, mas que roda fora da thread principal.
Service Workers devem ser registrados a partir de uma página que o usuário visite:
// main.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
Após o registro, o navegador cuida do ciclo install/activate. Seu sw.js pode ouvir eventos como install, activate e fetch.
Service Workers podem interceptar requisições e cachear respostas. Se o registro fosse permitido via HTTP, um atacante na rede poderia injetar um sw.js malicioso e efetivamente controlar visitas futuras. HTTPS (ou http://localhost para desenvolvimento) protege o script e o tráfego que ele pode influenciar.
Navegadores cacheiam e atualizam workers de forma diferente de scripts normais de página. Planeje atualizações:
sw.js/bundle do worker).Se quiser uma estratégia de rollout mais suave, veja /blog/debugging-workers para hábitos de teste que pegam casos de atualização cedo.
Workers falham de maneiras diferentes do JavaScript “normal” de página: rodando em contextos separados, com seu próprio console, e podendo ser reiniciados pelo navegador. Uma rotina sólida de depuração economiza horas.
Abra o DevTools e procure alvos específicos de worker. No Chrome/Edge, você frequentemente verá workers listados em Sources (ou via a entrada “Dedicated worker”) e no seletor de contexto do Console.
Use as mesmas ferramentas da thread principal:
onmessage e funções de longa execução.Se mensagens parecem “perdidas”, inspecione ambos os lados: verifique se você está chamando worker.postMessage(...), se o worker tem self.onmessage = ..., e se a forma da mensagem bate entre os dois.
Service Workers são melhor depurados no painel Application:
Também observe o Console para erros de install/activate/fetch — eles frequentemente explicam porque o cache ou o comportamento offline não funcionam.
Problemas de cache são o maior sorvedouro de tempo: cachear os arquivos errados (ou com muita agressividade) pode manter HTML/JS antigos. Durante testes, faça hard reload e confirme o que está sendo servido do cache.
Para testes realistas, use o DevTools para:
Se você está iterando rápido em um PWA, pode ajudar gerar um app base limpo (com um Service Worker e output de build previsíveis) e então refinar estratégias de cache. Plataformas como Koder.ai podem ser úteis para esse tipo de experimentação: você pode prototipar um app React a partir de um prompt de chat, exportar o código e ajustar os workers e regras de cache com um ciclo de feedback mais rápido.
Workers podem deixar apps mais suaves e capazes, mas também mudam onde o código roda e o que ele pode acessar. Uma checagem rápida em segurança, privacidade e performance evita bugs-surpresa — e usuários insatisfeitos.
Tanto Web Workers quanto Service Workers são restritos pela política de mesma origem: eles só podem interagir diretamente com recursos da mesma scheme/host/porta (a menos que o servidor permita acesso cross-origin via CORS). Isso evita que um worker puxe silenciosamente dados de outro site e misture na sua app.
Service Workers têm proteções adicionais: geralmente exigem HTTPS (ou localhost no desenvolvimento) porque podem interceptar requisições de rede. Trate-os como código privilegiado: mantenha dependências mínimas, evite carregar código dinamicamente e versionar a lógica de cache com cuidado para que caches antigos não sigam servindo arquivos desatualizados.
Recursos em segundo plano devem ser previsíveis. Push notifications são poderosas, mas prompts de permissão são fáceis de abusar.
Peça permissão apenas quando houver um benefício claro (por exemplo, depois que o usuário habilitar alertas nas configurações) e explique o que será recebido. Se você sincroniza ou pré-busca dados em background, comunique isso de forma clara — usuários notam atividade de rede inesperada ou notificações.
Workers não são “performance grátis”. Usá-los em excesso pode ter efeito contrário:
postMessage (especialmente com objetos grandes) podem se tornar gargalos. Prefira agrupar e usar objetos transferíveis quando apropriado.Nem todo navegador suporta todas as capacidades (ou usuários podem bloquear permissões). Faça feature-detect e degrade de forma limpa:
if ('serviceWorker' in navigator) {
// register service worker
} else {
// continue without offline features
}
O objetivo: a funcionalidade principal deve continuar funcionando, com “extras” (offline, push, computação pesada) adicionados quando disponíveis.
Web Workers e Service Workers resolvem problemas diferentes, então eles se complementam bem quando uma app precisa tanto de computação pesada quanto de carregamento rápido e confiável. Um bom modelo mental é: Web Worker = computação, Service Worker = rede + cache, thread principal = UI.
Imagine uma app que permite editar fotos (redimensionar, filtros, remoção de fundo) e ver uma galeria depois sem conexão.
Essa abordagem “computar e depois cachear” mantém responsabilidades claras: o worker gera saídas e o service worker decide como armazená-las e servi-las.
Para apps com feeds, formulários ou dados de campo:
Mesmo sem sync de background completo, um service worker ainda melhora a velocidade percebida ao servir respostas cacheadas enquanto a app atualiza em segundo plano.
Evite misturar papéis:
postMessage).Não. Um Service Worker roda em segundo plano, separado de qualquer aba de página, e não tem acesso direto ao DOM (os elementos HTML da página).
Essa separação é intencional: Service Workers foram projetados para continuar funcionando mesmo quando nenhuma página está aberta (por exemplo, para responder a um evento de push ou servir arquivos em cache). Como pode não haver nenhum documento ativo para manipular, o navegador mantém esse isolamento.
Se um Service Worker precisa afetar o que o usuário vê, ele se comunica com as páginas via mensagens (por exemplo, postMessage) para que a página atualize a UI.
Não. Web Workers e Service Workers são independentes.
Você pode usar apenas um, ou ambos quando sua app precisar de computação e funcionalidades de rede/offline.
Em navegadores modernos, Web Workers têm amplo suporte e costumam ser a escolha “base”.
Service Workers também são amplamente suportados nas versões atuais dos principais navegadores, mas há mais requisitos e casos de borda:
localhost para desenvolvimento).Se compatibilidade ampla for importante, trate Service Worker como aprimoramento progressivo: construa uma boa experiência central primeiro e adicione offline/push quando disponível.
Não automaticamente.
Os ganhos reais vêm de usar o worker certo para o gargalo certo, e medir antes e depois.
Use um Web Worker quando você tiver trabalho pesado de CPU que possa ser expresso como entrada → computação → saída e que não precise do DOM.
Boas aplicações incluem parsing/transformação de payloads grandes, compressão, criptografia, processamento de imagem/áudio e filtragem complexa. Se o trabalho é majoritariamente atualizações de UI ou leituras/escritas frequentes no DOM, um worker não ajudará (e de qualquer forma não pode acessar o DOM).
Use um Service Worker quando você precisar de controle da rede: suporte offline, estratégias de cache, carregamentos mais rápidos em visitas repetidas, roteamento de requisições e (quando suportado) push/background sync.
Se o seu problema é “a UI congela enquanto computa”, isso é caso para Web Worker. Se o problema é “o carregamento é lento/ou o offline não funciona”, isso é caso para Service Worker.
Não. Web Workers e Service Workers são recursos independentes.
Você pode usar apenas um, ou combiná-los quando precisar tanto de computação quanto de funcionalidades de rede/offline.
Principalmente escopo e tempo de vida.
fetch) mesmo quando nenhuma aba está aberta, e depois encerrá-lo quando ficar ocioso.Não. Web Workers não têm acesso a window/document.
Se você precisa afetar a interface, envie os dados de volta à thread principal via postMessage() e atualize o DOM no código da página. Mantenha o worker focado em computação pura.
Não. Service Workers também não têm acesso ao DOM.
Para influenciar o que o usuário vê, comunique-se com as páginas controladas via mensagens (por exemplo usando a Clients API + postMessage()), e deixe a página atualizar a UI.
Use postMessage() em ambos os lados.
worker.postMessage(data)self.postMessage(result)Para dados binários grandes, prefira transferíveis (como ArrayBuffer) para evitar cópias:
Service Workers ficam entre sua app e a rede e podem responder a requisições usando a Cache Storage API.
Estratégias comuns:
Escolha a estratégia por tipo de recurso (app shell vs dados de API), não uma regra única para tudo.
Sim — mas mantenha responsabilidades separadas.
Um padrão comum é:
Isso evita misturar lógica de UI em contextos em segundo plano e mantém o desempenho previsível.
Use a superfície certa do DevTools para cada caso.
onmessage e faça profiling para confirmar que a thread principal permanece responsiva.Ao depurar problemas de cache, confirme sempre o que está sendo servido (network vs cache) e teste em modo offline/throttling.
worker.postMessage(buffer, [buffer]);