Cancelar pedidos com AbortController
Um pedido que já ninguém quer continua a ocupar ligação, a gastar bateria, e a chegar atrasado para escrever por cima de um resultado mais recente. O AbortController é a forma padrão de o cortar a meio — e não serve só para o fetch.
1. O básico
const controlador = new AbortController();
fetch('/api/artigos', { signal: controlador.signal })
.then((resposta) => resposta.json())
.catch((erro) => {
if (erro.name === 'AbortError') return; // fomos nós que cancelámos
throw erro;
});
// Noutro sítio qualquer: já não interessa
controlador.abort();2. Um tempo limite, sem contar o tempo à mão
O AbortSignal.timeout() devolve um sinal que se cancela sozinho. Repare no nome do erro: é TimeoutError, e não AbortError — dá para distinguir "demorou de mais" de "cancelámos nós".
try {
const resposta = await fetch('/api/artigos', {
signal: AbortSignal.timeout(5000),
});
console.log(await resposta.json());
} catch (erro) {
if (erro.name === 'TimeoutError') {
console.log('O servidor demorou mais de 5 segundos.');
}
}3. Dois motivos para desistir
O AbortSignal.any() junta vários sinais, e o primeiro que dispare cancela o pedido. Serve para "cinco segundos ou a pessoa saiu da página":
const saiuDaPagina = new AbortController();
const resposta = await fetch('/api/artigos', {
signal: AbortSignal.any([saiuDaPagina.signal, AbortSignal.timeout(5000)]),
});4. Também serve para ouvintes de eventos
Esta é a parte que passa despercebida: o addEventListener aceita um sinal, e cancelá-lo remove o ouvinte. Um controlador limpa tudo de uma vez, sem ser preciso guardar referências às funções:
const limpeza = new AbortController();
window.addEventListener('resize', aoRedimensionar, { signal: limpeza.signal });
window.addEventListener('scroll', aoDeslizar, { signal: limpeza.signal });
// Uma chamada só, e os dois ouvintes desaparecem
limpeza.abort();