“Precisa atualizar sem recarregar a página” parece uma frase curta. O problema é que ela costuma empurrar a arquitetura direto para WebSocket, como se qualquer tela viva precisasse de um canal bidirecional aberto. Na prática, muita coisa chamada de tempo real tolera alguns segundos de atraso, recebe dados em uma direção só ou nem fica aberta tempo suficiente para justificar uma conexão persistente.
Polling, Server-Sent Events (SSE) e WebSocket não são três degraus da mesma escada. São respostas para fluxos diferentes. A escolha começa por quatro perguntas: quem inicia a comunicação, com que frequência os dados mudam, quanto atraso o produto aceita e o que a equipe consegue operar quando a conexão cai.
Polling não é uma vergonha arquitetural
No polling, o navegador pergunta periodicamente se há novidade. É HTTP comum, passa por infraestrutura conhecida, funciona bem com autenticação, observabilidade e cache, e não mantém uma conexão aberta por usuário. Para um painel atualizado a cada 30 segundos, o status de uma exportação ou uma caixa de notificações, isso pode ser exatamente o necessário.
async function atualizarStatus() {
try {
const resposta = await fetch('/api/exportacoes/42');
const status = await resposta.json();
renderizar(status);
} finally {
setTimeout(atualizarStatus, 5000);
}
}
atualizarStatus();
Usar setTimeout depois que a requisição termina evita empilhar chamadas quando o servidor demora mais que o intervalo. Também vale pausar ou reduzir a frequência com a aba em segundo plano, aumentar o intervalo após erros e responder com ETag ou Last-Modified quando isso fizer sentido.
O custo aparece quando a frequência é alta e quase todas as respostas dizem “nada mudou”. Mil clientes consultando a cada segundo viram mil requisições por segundo mesmo num dia parado. Se a exigência é entregar eventos assim que surgem, o modelo começa a gastar trabalho só para descobrir o silêncio.
SSE é uma ótima resposta para servidor → navegador
Server-Sent Events mantém uma resposta HTTP aberta no formato text/event-stream. O servidor envia eventos conforme eles aparecem e o cliente usa a API EventSource. O canal é de mão única: servidor para navegador. O cliente continua podendo enviar comandos com fetch.
const eventos = new EventSource('/api/builds/stream');
eventos.addEventListener('status', (event) => {
const build = JSON.parse(event.data);
atualizarBuild(build);
});
eventos.onerror = () => {
mostrarAviso('Tentando reconectar…');
};
A API reconecta automaticamente quando a conexão é interrompida. O protocolo também prevê os campos id e retry; com um identificador por evento, o navegador pode enviar Last-Event-ID ao reconectar. Isso ajuda a retomar o fluxo sem perder o que aconteceu no intervalo, desde que o servidor mantenha um histórico ou cursor.
SSE combina com progresso de jobs, logs ao vivo, cotações, feeds e notificações. Ainda há trabalho operacional: proxies precisam aceitar respostas longas, buffers podem atrasar eventos, keep-alives evitam conexões consideradas ociosas e a autenticação precisa respeitar as limitações da interface EventSource. Em HTTP/1, navegadores também impõem um limite baixo de conexões por origem; HTTP/2 negocia vários streams e alivia esse ponto.
WebSocket entra quando os dois lados realmente conversam
WebSocket abre um canal full-duplex: navegador e servidor podem mandar mensagens a qualquer momento. É o encaixe natural para chat, edição colaborativa, jogos, presença online e experiências em que comandos e eventos circulam continuamente nas duas direções.
A flexibilidade cobra seu preço. A aplicação define formato das mensagens, correlação entre pedido e resposta, autorização por ação, heartbeat, retomada de sessão e estratégia de reconexão. Load balancers e gateways precisam aceitar o upgrade da conexão. Escalar horizontalmente costuma exigir uma camada de pub/sub ou algum modo de localizar a sessão certa.
A API clássica de WebSocket no navegador também não oferece backpressure. Se mensagens chegam mais rápido do que a aplicação processa, memória e CPU podem sofrer. E uma conexão aberta deve ser tratada no ciclo de vida da página: a documentação da MDN recomenda fechar no evento pagehide, inclusive para não atrapalhar o back/forward cache. Em produção, use wss://.
Uma regra prática que cabe na reunião
- Escolha polling quando segundos ou minutos de atraso são aceitáveis, a mudança é pouco frequente e simplicidade pesa mais.
- Escolha SSE quando o servidor precisa empurrar uma sequência de atualizações para o navegador, mas os comandos do cliente cabem em HTTP normal.
- Escolha WebSocket quando há conversa frequente nos dois sentidos e a latência faz parte da experiência.
O teste mais honesto é desenhar as setas. Se quase todas apontam do servidor para o cliente, WebSocket talvez seja complexidade antecipada. Se o produto aceita ver a mudança na próxima consulta, SSE talvez também seja demais. E, qualquer que seja a tecnologia, “tempo real” só fica confiável quando há uma resposta para reconexão, duplicidade, ordenação, autenticação e observabilidade.
