Teste de origem da API fetchLater

Brendan Kenny
Brendan Kenny

É comum que as páginas da Web precisem enviar dados (ou "beacon") de volta ao servidor. Por exemplo, nos 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 dados perdidos se a guia for fechada ou o usuário sair antes do envio de um beacon.

Tradicionalmente, os desenvolvedores usavam eventos pagehide e visibilitychange para capturar a página durante o descarregamento e depois usavam navigator.sendBeacon() ou fetch() com keepalive para sinalizar os dados. No entanto, esses dois eventos têm casos específicos que diferem com base no navegador do usuário e, às vezes, os eventos nunca chegam, especialmente em dispositivos móveis.

fetchLater() é uma proposta para substituir essa complexidade por uma única chamada de API. Ele faz exatamente o que o nome sugere: pede ao navegador para garantir que uma solicitação seja feita em algum momento no futuro, mesmo que a página seja fechada ou o usuário saia.

O 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) até o Chrome 126 (julho 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 de Request.
  • Um objeto options opcional, que estende o options do fetch() com um tempo limite chamado activateAfter.

fetchLater() retorna um FetchLaterResult, atualmente contendo apenas uma única propriedade somente leitura activated, que será definida como true quando "mais tarde" tiver passado e a busca tiver sido feita. Qualquer resposta à solicitação fetchLater() será descartada.

request

O uso mais simples é um URL sozinho:

fetchLater('/endpoint/');

Mas, assim como 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 potencialmente 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, é possível escolher entre receber os dados no último momento possível absoluto ou quando é mais oportuno.

Por exemplo, se você tem um aplicativo que os usuários geralmente mantêm aberto o dia todo, convém definir um tempo limite de uma hora para garantir uma análise mais granular e, ao mesmo tempo, garantir um beacon se o usuário sair a qualquer momento antes desse período. É possível configurar um novo fetchLater() para a próxima hora de análise.

const hourInMilliseconds = 60 * 60 * 1000;
fetchLater('/endpoint/', {activateAfter: hourInMilliseconds});

Exemplo de uso

Um problema ao avaliar as Core Web Vitals no campo é que qualquer uma das métricas de desempenho pode mudar até que o usuário realmente saia de uma página. Por exemplo, mudanças maiores de 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 beacons incompletos ou com falhas no descarregamento da página. É um candidato perfeito para a 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 há uma atualização de métrica, qualquer fetchLater() programado existente é cancelado com um AbortController, e um novo fetchLater() é criado com a atualização incluída.

Teste o fetchLater()

Como informado, o fetchLater() está disponível em um teste de origem até o Chrome 126. Consulte Primeiros passos com testes de origem para saber mais informações sobre esses testes.

Para testes locais, a propriedade fetchLater pode ser ativada com a flag de recursos da Plataforma Web experimental em chrome://flags/#enable-experimental-web-platform-features. Ela também pode ser ativada executando o Chrome na linha de comando com --enable-experimental-web-platform-features ou a sinalização --enable-features=FetchLaterAPI mais direcionada.

Se você usá-lo em uma página pública, faça a detecção do recurso verificando se a fetchLater global está definida antes de usá-la:

if (globalThis.fetchLater) {
  // Set up beaconing using fetchLater().
  // ...
}

Feedback

O feedback do desenvolvedor é essencial para ter novas APIs da Web da maneira certa. Portanto, envie feedback e problemas no GitHub.

Mais informações