É comum que as páginas da Web precisem enviar dados (ou "beacons") de volta ao servidor. Por exemplo, dados de análise da sessão atual de um usuário. Para os desenvolvedores, isso exige um equilíbrio: reduzir solicitações constantes e possivelmente redundantes sem arriscar a perda de dados se a guia for fechada ou o usuário sair antes que um beacon seja enviado.
Tradicionalmente, os desenvolvedores usavam os eventos pagehide
e visibilitychange
para capturar a página durante o descarregamento e, em seguida, usavam navigator.sendBeacon()
ou um fetch()
com keepalive
para dados de beacon. No entanto, esses dois eventos têm casos difíceis que diferem de acordo com o navegador do usuário e, às vezes, os eventos nem chegam, principalmente em dispositivos móveis.
fetchLater()
é uma proposta para substituir essa complexidade por uma única chamada de API. Ela faz exatamente o que o nome sugere: pede ao navegador para garantir que uma solicitação seja feita no futuro, mesmo que a página seja fechada ou o usuário saia dela.
fetchLater()
está disponível no Chrome para testes com usuários reais em um teste de origem a partir da versão 121 (lançada em janeiro de 2024) e vai durar até 3 de setembro de 2024.
A API fetchLater()
const fetchLaterResult = fetchLater(request, options);
fetchLater()
usa dois argumentos, geralmente idênticos aos de fetch()
:
- O
request
, um URL de string ou uma instância deRequest
. - Um objeto
options
opcional, que estende ooptions
defetch()
com um tempo limite chamadoactivateAfter
.
fetchLater()
retorna um FetchLaterResult
, que atualmente contém apenas uma única propriedade activated
somente leitura, que será definida como true
quando "mais tarde" tiver passado e a busca tiver sido feita. Qualquer resposta à solicitação fetchLater()
é descartada.
request
O uso mais simples é um URL sozinho:
fetchLater('/endpoint/');
No entanto, assim como em fetch()
, um grande número de opções pode ser definido em uma solicitação fetchLater()
, incluindo cabeçalhos personalizados, comportamento de credenciais, um corpo POST
e um AbortController
signal
para possivelmente cancelá-la.
fetchLater('/endpoint/', {
method: 'GET',
cache: 'no-store',
mode: 'same-origin',
headers: {Authorization: 'SUPER_SECRET'},
});
options
O objeto de opções estende as opções de fetch()
com um tempo limite, activateAfter
, caso você queira disparar a solicitação após o tempo limite ou quando a página for descarregada, o que ocorrer primeiro.
Assim, você pode decidir entre receber dados no último momento possível ou quando for mais oportuno.
Por exemplo, se você tiver um app que os usuários geralmente mantêm aberto durante todo o dia de trabalho, talvez seja melhor definir um tempo limite de uma hora para garantir análises mais detalhadas e, ao mesmo tempo, garantir um beacon se o usuário sair a qualquer momento antes desse período. Uma nova fetchLater()
pode ser configurada para a próxima hora de análise.
const hourInMilliseconds = 60 * 60 * 1000;
fetchLater('/endpoint/', {activateAfter: hourInMilliseconds});
Exemplo de uso
Um problema ao medir as Core Web Vitals no campo é que qualquer uma das métricas de performance pode mudar até que o usuário realmente saia de uma página. Por exemplo, mudanças maiores no layout podem acontecer a qualquer momento, ou a página pode levar ainda mais tempo para responder a uma interação.
No entanto, você não quer correr o risco de perder todos os dados de desempenho devido a um beacon com bugs ou incompleto no encerramento da página. É o candidato perfeito para fetchLater()
.
Neste exemplo, a biblioteca web-vitals.js é usada para monitorar as métricas, e fetchLater()
é usada para informar os resultados a um endpoint de análise:
import {onCLS, onINP, onLCP} from 'web-vitals';
const queue = new Set();
let fetchLaterController;
let fetchLaterResult;
function updateQueue(metricUpdate) {
// If there was an already complete request for whatever
// reason, clear out the queue of already-sent updates.
if (fetchLaterResult?.activated) {
queue.clear();
}
queue.add(metricUpdate);
// JSON.stringify used here for simplicity and will likely include
// more data than you need. Replace with a preferred serialization.
const body = JSON.stringify([...queue]);
// Abort any existing `fetchLater()` and schedule a new one with
// the update included.
fetchLaterController?.abort();
fetchLaterController = new AbortController();
fetchLaterResult = fetchLater('/analytics', {
method: 'POST',
body,
signal: fetchLaterController.signal,
activateAfter: 60 * 60 * 1000, // Timeout to ensure timeliness.
});
}
onCLS(updateQueue);
onINP(updateQueue);
onLCP(updateQueue);
Sempre que uma atualização de métrica é recebida, qualquer fetchLater()
programado é cancelado com um AbortController
e um novo fetchLater()
é criado com a atualização incluída.
Teste o fetchLater()
Como mencionado, fetchLater()
está disponível em um teste de origem até o Chrome 126. Consulte Começar a usar os testes de origem para mais informações sobre eles.
Para testes locais, fetchLater
pode ser ativado com a flag de recursos experimentais da plataforma da Web em chrome://flags/#enable-experimental-web-platform-features
. Também é possível ativar essa opção executando o Chrome na linha de comando com --enable-experimental-web-platform-features
ou a flag --enable-features=FetchLaterAPI
mais específica.
Se você usar o recurso em uma página pública, verifique se o fetchLater
global está definido antes de usá-lo:
if (globalThis.fetchLater) {
// Set up beaconing using fetchLater().
// ...
}
Feedback
O feedback dos desenvolvedores é essencial para que as novas APIs da Web funcionem corretamente. Registre problemas e feedback no GitHub.