JavaScript Avançado

AbortController e Cancelamento de Operações Assíncronas: Do Básico ao Avançado

7 min de leitura

AbortController e Cancelamento de Operações Assíncronas: Do Básico ao Avançado

AbortController: Dominando o Cancelamento de Operações Assíncronas O é uma API moderna do JavaScript que permite cancelar operações assíncronas como requisições fetch, timers e streams. Antes dessa API, não havia um padrão consistente para interromper operações em andamento, levando a vazamento de memória e comportamentos inesperados. Entender como implementá-lo corretamente é essencial para construir aplicações robustas e eficientes. Por que precisamos cancelar operações? Considere um cenário real: um usuário digita em um campo de busca, mas muda de ideia antes de clicar em buscar. Se a requisição foi feita e ainda está em trânsito, você quer cancelá-la para evitar processamento desnecessário, atualizar a UI com dados obsoletos ou desperdiçar banda de rede. O resolve exatamente esse problema. Conceitos Fundamentais O que é AbortController? é um objeto que fornece dois componentes: o controlador ( ) e o sinal ( ). O controlador permite enviar um comando de cancelamento, enquanto o sinal é passado para a operação assíncrona que será monitorada. Anatomia

<h2>AbortController: Dominando o Cancelamento de Operações Assíncronas</h2>

<p>O <code>AbortController</code> é uma API moderna do JavaScript que permite cancelar operações assíncronas como requisições fetch, timers e streams. Antes dessa API, não havia um padrão consistente para interromper operações em andamento, levando a vazamento de memória e comportamentos inesperados. Entender como implementá-lo corretamente é essencial para construir aplicações robustas e eficientes.</p>

<h3>Por que precisamos cancelar operações?</h3>

<p>Considere um cenário real: um usuário digita em um campo de busca, mas muda de ideia antes de clicar em buscar. Se a requisição foi feita e ainda está em trânsito, você quer cancelá-la para evitar processamento desnecessário, atualizar a UI com dados obsoletos ou desperdiçar banda de rede. O <code>AbortController</code> resolve exatamente esse problema.</p>

<h2>Conceitos Fundamentais</h2>

<h3>O que é AbortController?</h3>

<p><code>AbortController</code> é um objeto que fornece dois componentes: o controlador (<code>AbortController</code>) e o sinal (<code>AbortSignal</code>). O controlador permite enviar um comando de cancelamento, enquanto o sinal é passado para a operação assíncrona que será monitorada.</p>

<pre><code class="language-javascript">const controller = new AbortController();

const signal = controller.signal;

// signal é passado para operações que o suportam

// controller.abort() cancela tudo que está escutando esse signal</code></pre>

<h3>Anatomia básica</h3>

<pre><code class="language-javascript">const controller = new AbortController();

// Monitorar se a operação foi abortada

controller.signal.addEventListener(&#039;abort&#039;, () =&gt; {

console.log(&#039;Operação cancelada!&#039;);

});

// Cancelar após 3 segundos

setTimeout(() =&gt; controller.abort(), 3000);

// Verificar se já foi abortado

console.log(controller.signal.aborted); // true/false</code></pre>

<h2>Usando AbortController com Fetch</h2>

<h3>Cancelamento manual de requisições</h3>

<p>O caso mais comum é cancelar requisições HTTP. Veja como implementar corretamente:</p>

<pre><code class="language-javascript">const controller = new AbortController();

// Começar requisição

fetch(&#039;https://api.example.com/dados&#039;, {

signal: controller.signal

})

.then(response =&gt; response.json())

.then(data =&gt; console.log(data))

.catch(error =&gt; {

if (error.name === &#039;AbortError&#039;) {

console.log(&#039;Requisição foi cancelada&#039;);

} else {

console.error(&#039;Erro na requisição:&#039;, error);

}

});

// Cancelar após 5 segundos

setTimeout(() =&gt; controller.abort(), 5000);</code></pre>

<h3>Timeout automático com AbortSignal.timeout()</h3>

<p>A forma mais elegante é usar <code>AbortSignal.timeout()</code>, que cancela automaticamente após um tempo específico:</p>

<pre><code class="language-javascript">// Cancela automaticamente após 5 segundos

fetch(&#039;https://api.example.com/dados&#039;, {

signal: AbortSignal.timeout(5000)

})

.then(response =&gt; response.json())

.then(data =&gt; console.log(data))

.catch(error =&gt; {

if (error.name === &#039;AbortError&#039;) {

console.log(&#039;Requisição expirou&#039;);

}

});</code></pre>

<h2>Padrões Avançados</h2>

<h3>Gerenciar múltiplas operações com um único sinal</h3>

<p>Você pode reutilizar o mesmo sinal para cancelar várias operações em conjunto:</p>

<pre><code class="language-javascript">const controller = new AbortController();

Promise.all([

fetch(&#039;/api/usuarios&#039;, { signal: controller.signal }),

fetch(&#039;/api/posts&#039;, { signal: controller.signal }),

fetch(&#039;/api/comentarios&#039;, { signal: controller.signal })

])

.then(responses =&gt; Promise.all(responses.map(r =&gt; r.json())))

.then(data =&gt; console.log(&#039;Todos os dados carregados:&#039;, data))

.catch(error =&gt; {

if (error.name === &#039;AbortError&#039;) {

console.log(&#039;Uma ou mais requisições foram canceladas&#039;);

}

});

// Um cancel afeta todas as requisições

setTimeout(() =&gt; controller.abort(), 3000);</code></pre>

<h3>Combinar sinais com AbortSignal.any()</h3>

<p>Para cenários onde você quer que a primeira operação que terminar vença, use <code>AbortSignal.any()</code>:</p>

<pre><code class="language-javascript">const userController = new AbortController();

const timeoutSignal = AbortSignal.timeout(10000);

// O que terminar primeiro (usuário cancela ou timeout de 10s)

const combinedSignal = AbortSignal.any([

userController.signal,

timeoutSignal

]);

fetch(&#039;/api/dados-pesados&#039;, { signal: combinedSignal })

.then(r =&gt; r.json())

.then(data =&gt; console.log(data))

.catch(error =&gt; {

if (error.name === &#039;AbortError&#039;) {

console.log(&#039;Operação cancelada ou expirou&#039;);

}

});

// Usuário pode cancelar manualmente

document.getElementById(&#039;cancelBtn&#039;).addEventListener(&#039;click&#039;, () =&gt; {

userController.abort();

});</code></pre>

<h3>Implementação em uma classe real</h3>

<pre><code class="language-javascript">class SearchAPI {

constructor() {

this.controller = null;

}

async buscar(termo) {

// Cancelar busca anterior se houver

if (this.controller) {

this.controller.abort();

}

this.controller = new AbortController();

try {

const response = await fetch(

https://api.example.com/search?q=${termo},

{ signal: this.controller.signal }

);

const data = await response.json();

return data;

} catch (error) {

if (error.name === &#039;AbortError&#039;) {

console.log(&#039;Busca cancelada&#039;);

} else {

throw error;

}

}

}

cancelar() {

if (this.controller) {

this.controller.abort();

}

}

}

// Uso

const search = new SearchAPI();

search.buscar(&#039;javascript&#039;);

setTimeout(() =&gt; search.cancelar(), 2000);</code></pre>

<h2>Conclusão</h2>

<p><strong>Três aprendizados principais:</strong></p>

<ol>

<li><strong>AbortController resolve um problema real</strong>: evita requisições fantasma, vazamento de memória e comportamentos inesperados em aplicações modernas. Use-o sempre que trabalhar com operações assíncronas que podem ser interrompidas.</li>

</ol>

<ol>

<li><strong>Use <code>AbortSignal.timeout()</code> para timeouts simples</strong>: não precisa de lógica complexa com <code>setTimeout</code>. A API oferece exatamente o que você precisa de forma limpa.</li>

</ol>

<ol>

<li><strong>Combine sinais para cenários complexos</strong>: <code>AbortSignal.any()</code> permite orquestrar múltiplas operações com elegância, cobrindo casos onde timeout do usuário e timeout automático precisam coexistir.</li>

</ol>

<p>A prática consistente com essas técnicas transformará suas aplicações em código mais profissional e confiável.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/AbortController" target="_blank" rel="noopener noreferrer">MDN Web Docs - AbortController</a></li>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal" target="_blank" rel="noopener noreferrer">MDN Web Docs - AbortSignal</a></li>

<li><a href="https://github.com/tc39/proposal-promise-with-resolvers" target="_blank" rel="noopener noreferrer">TC39 - Promise.withResolvers Proposal</a></li>

<li><a href="https://javascript.info/fetch" target="_blank" rel="noopener noreferrer">JavaScript.info - Fetch API</a></li>

<li><a href="https://web.dev/abort-signals/" target="_blank" rel="noopener noreferrer">Web.dev - Abort signals in JavaScript</a></li>

</ul>

Comentários

Mais em JavaScript Avançado

Performance em React: memo, useMemo, useCallback e Profiler na Prática
Performance em React: memo, useMemo, useCallback e Profiler na Prática

Performance em React: memo, useMemo, useCallback e Profiler React é declarati...

Como Usar Testes End-to-End com Playwright: Page Object Model e CI Integration em Produção
Como Usar Testes End-to-End com Playwright: Page Object Model e CI Integration em Produção

O que é Playwright e Por Que Page Object Model? Playwright é um framework de...

Mutation Testing em JavaScript: Stryker e Qualidade Real da Suíte na Prática
Mutation Testing em JavaScript: Stryker e Qualidade Real da Suíte na Prática

O Que é Mutation Testing e Por Que Importa Mutation testing é uma técnica de...