Fetch: abort
Aprenda a cancelar requisições fetch em JavaScript com AbortController e AbortSignal: cancelamentos por ação do usuário, timeouts, múltiplas requisições e tratamento correto de AbortError.
Uma vez que uma requisição fetch() está em andamento, ela continua executando até que o servidor responda — mesmo que o usuário tenha navegado para outra página, digitado um novo termo de busca ou a resposta não seja mais necessária. Essas requisições desperdiçadas ocupam conexões, consomem bateria e largura de banda, e podem entregar resultados desatualizados que sobrescrevem os mais recentes. A interface AbortController oferece uma maneira limpa e padronizada de cancelar uma requisição sob demanda.
Este capítulo mostra como configurar o AbortController, reagir a ações do usuário, criar timeouts, cancelar várias requisições ao mesmo tempo e tratar o AbortError resultante corretamente. Para saber mais sobre o fetch em si, consulte a página anterior, Fetch API.
Como o AbortController funciona
AbortController é um objeto pequeno com uma única finalidade: ele possui um AbortSignal e pode mudar esse sinal para o estado abortado. O sinal é a parte que você passa para o fetch() (e para muitas outras APIs do navegador, como addEventListener). Quando você chama controller.abort(), toda operação que recebeu esse sinal é cancelada.
O padrão sempre segue os mesmos três passos:
- Criar um controller:
const controller = new AbortController(). - Passar
controller.signalpara ofetch()no objeto de opções. - Chamar
controller.abort()sempre que quiser cancelar.
Quando um fetch é abortado, sua promise rejeita com uma DOMException cujo name é "AbortError". É por isso que todos os exemplos abaixo verificam error.name === 'AbortError' — para que você possa ignorar o cancelamento intencional enquanto ainda expõe falhas reais de rede.
Uso básico do AbortController
Aqui está o menor exemplo completo. Ele aborta imediatamente para que você possa ver o caminho de rejeição:
Criamos um AbortController, passamos seu signal para o fetch() e imediatamente chamamos controller.abort(). Como a requisição nunca é concluída normalmente, o .catch() é executado e reporta o cancelamento.
Inspecionando o sinal: aborted e o evento abort
O sinal expõe seu estado para que outro código possa reagir a um cancelamento. Dois membros são mais importantes:
signal.aborted— um boolean que se tornatrueapós o cancelamento.- o evento
"abort"— disparado no sinal no momento em queabort()é chamado.
Isso é útil quando você tem trabalho que não envolve fetch (um timer, uma animação, um leitor de stream) que deve parar no momento em que a requisição é cancelada.
Um controller, um uso. Um controller não pode ser redefinido. Uma vez que você chame
abort(), esse sinal permanece abortado para sempre, e qualquer novofetch()iniciado com ele rejeita imediatamente. Para uma nova requisição, crie um novoAbortController.
Exemplo prático: abortando por ação do usuário
O motivo mais comum para abortar é o usuário mudar de ideia — clicar em "Cancelar", fechar um diálogo ou digitar uma nova consulta antes de a anterior terminar. Aqui, um botão cancela uma requisição em andamento:
<body>
<button id="abortButton">Abort Fetch Request</button>
<script>
const controller = new AbortController();
const signal = controller.signal;
document.getElementById('abortButton').addEventListener('click', () => {
controller.abort();
});
fetch('https://httpbin.org/delay/5', { signal })
.then(response => response.json())
.then(data => alert(
'Data is successfully fetched! Refresh the page and try aborting.'
))
.catch(error => {
if (error.name === 'AbortError') {
alert('Fetch request was aborted by the user');
} else {
alert('Fetch error: ' + error.message);
}
});
</script>
</body>Clicar no botão com o ID abortButton cancela a requisição fetch em andamento. O endpoint https://httpbin.org/delay/5 deliberadamente leva 5 segundos, portanto, se você clicar dentro desse intervalo, a requisição rejeita com AbortError.
Abortando várias requisições de uma vez
Um único sinal pode ser passado para várias requisições. Uma única chamada a controller.abort() cancela todas elas — útil quando uma página sai de uma view que iniciou vários carregamentos paralelos:
Como as três requisições compartilham um único sinal, controller.abort() cancela todas em uma única chamada e o Promise.all rejeita com AbortError. Para saber mais sobre execução de requisições em paralelo, consulte Promise API.
Abortando após um timeout
Um uso muito comum do AbortController é dar um prazo a uma requisição: se o servidor for muito lento, cancelar e exibir um erro em vez de esperar indefinidamente.
A forma manual com setTimeout
Você pode combinar o controller com um timer e integrá-lo com qualquer outra lógica assíncrona. Aqui, uma operação separada dispara o abort após um segundo, enquanto o endpoint levaria cinco:
O fetch é abortado pelo timer após um segundo, muito antes de o endpoint de cinco segundos poder responder. Leia mais sobre timers em async/await.
O atalho: AbortSignal.timeout()
Navegadores modernos (e Node 17.3+) incluem um helper integrado que cria um sinal que se aborta automaticamente após um número determinado de milissegundos — sem necessidade de controller ou setTimeout:
Note que um abort por timeout rejeita com TimeoutError, e não com AbortError, então você pode distinguir "demorou demais" de "o usuário cancelou". Se você precisar de ambos — um timeout e um botão de cancelamento manual —, combine os sinais com AbortSignal.any([userSignal, AbortSignal.timeout(2000)]).
Tratando AbortError corretamente
Sempre que você aborta um fetch, a promise rejeita. Esquecer de tratar isso produz um ruidoso "uncaught promise rejection" no console, mesmo que o cancelamento tenha sido intencional. Duas regras mantêm as coisas organizadas:
- Sempre inclua um
.catch()(outry/catchcomawait) em um fetch que pode ser abortado. - Dentro dele, verifique
error.namee trate'AbortError'/'TimeoutError'como esperado — registre ou exiba apenas os outros erros. Consulte Tratamento de erros com promises para o padrão mais amplo.
Conclusão
AbortController é a forma padrão de cancelar requisições fetch() em JavaScript. Crie um controller, passe seu signal para uma ou mais requisições e chame abort() sempre que o trabalho não for mais necessário. Você viu cancelamento iniciado pelo usuário, timeouts manuais e integrados, cancelamento de várias requisições ao mesmo tempo e como tratar o AbortError resultante. Adotar esses padrões mantém suas aplicações responsivas e evita que desperdicem tempo com resultados que ninguém mais aguarda.