Melhorar a dispensa de páginas em XMLHttpRequest síncrono

Joe Medley
Joe Medley

É comum que uma página ou um app tenha dados de análise ou outros dados não enviados quando um usuário o fecha. Para evitar a perda de dados, alguns sites usam uma chamada síncrona para XMLHttpRequest() para manter a página ou o app aberto até que os dados sejam transmitidos ao servidor. Além de haver maneiras melhores de salvar dados, essa técnica cria uma experiência ruim para o usuário, atrasando o fechamento da página em até vários segundos.

Essa prática precisa mudar, e os navegadores estão respondendo. A XMLHttpRequest() especificação já está programada para descontinuação e remoção. O Chrome 80 dá o primeiro passo ao proibir chamadas síncronas em vários gerenciadores de eventos, especificamente beforeunload, unload, pagehide e visibilitychange, quando são acionados na dispensa. O WebKit também lançou recentemente um commit que implementa a mesma mudança de comportamento.

Vou descrever brevemente as opções para quem precisa de tempo para atualizar os sites e descrever as alternativas para XMLHttpRequest().

Desativações temporárias

O Chrome quer oferecer tempo aos desenvolvedores para remover a dependência de XMLHttpRequest(). Por isso, oferecemos opções de desativação temporária.

Participe de um teste de origem. Com isso, você adiciona um token específico da origem aos cabeçalhos da página que permite chamadas síncronas XMLHttpRequest(). Essa opção termina pouco antes do lançamento do Chrome 89, em março de 2021. Os clientes corporativos do Chrome também podem usar a flag de política AllowSyncXHRInPageDismissal, que termina ao mesmo tempo.

Alternativas

Independente de como você envia dados de volta ao servidor, é melhor evitar esperar até o descarregamento da página para enviar todos os dados de uma só vez. Além de criar uma experiência ruim para o usuário, o descarregamento não é confiável em navegadores modernos e corre o risco de perda de dados se algo der errado. Especificamente, os eventos de descarregamento geralmente não são acionados em navegadores para dispositivos móveis porque há muitas maneiras de fechar uma guia ou navegador em sistemas operacionais móveis sem que o evento unload seja acionado. Com XMLHttpRequest(), o uso de payloads pequenos era uma opção. Agora é um requisito. As duas alternativas têm um limite de upload de 64 KB por contexto, conforme exigido pela especificação.

Fetch keepalive

A API Fetch oferece um meio robusto de lidar com interações do servidor e uma interface consistente para uso em diferentes APIs de plataforma. Entre as opções, está keepalive, que garante que uma solicitação continue, independentemente de a página que a fez permanecer aberta ou não:

window.addEventListener('unload', {
  fetch('/siteAnalytics', {
    method: 'POST',
    body: getStatistics(),
    keepalive: true
  });
}

O método fetch() tem a vantagem de maior controle sobre o que é enviado ao servidor. O que não mostro no exemplo é que fetch() também retorna uma promessa que é resolvida com um objeto Response. Como estou tentando sair do caminho do descarregamento da página, optei por não fazer nada com ela.

SendBeacon()

SendBeacon() usa a API Fetch nos bastidores. É por isso que ela tem a mesma limitação de payload de 64 KB e também garante que uma solicitação continue após o descarregamento de uma página. A principal vantagem é a simplicidade. Ela permite enviar dados com uma única linha de código:

window.addEventListener('unload', {
  navigator.sendBeacon('/siteAnalytics', getStatistics());
}

Conclusão

Com o aumento da disponibilidade de fetch() em navegadores, esperamos que XMLHttpRequest() seja removido da plataforma da Web. Os fornecedores de navegadores concordam que ele precisa ser removido, mas isso vai levar tempo. A descontinuação de um dos piores casos de uso é um primeiro passo que melhora a experiência do usuário para todos.