JavaScript

Boas Práticas de Tratamento de Erros Assíncronos em JavaScript na Prática para Times Ágeis

7 min de leitura

Boas Práticas de Tratamento de Erros Assíncronos em JavaScript na Prática para Times Ágeis

Entendendo Promises e seu Tratamento de Erros Uma Promise é a base do tratamento de erros assíncronos em JavaScript. Ela representa uma operação que pode estar pendente, resolvida ou rejeitada. Quando uma Promise é rejeitada, ela dispara um erro que precisa ser capturado. O método é a forma clássica de tratar esses erros, funcionando como um para operações assíncronos. Neste exemplo, o primeiro captura erros da requisição ou parsing JSON. Se houver um erro não tratado, o segundo será acionado. É importante entender que cada ou cria uma nova Promise, permitindo encadeamento seguro. Async/Await com Try-Catch O async/await é a sintaxe moderna e mais legível para trabalhar com Promises. Com ele, código assíncronos parece síncrono, e podemos usar tradicionais para tratar erros. Esta é a abordagem recomendada para novos projetos. https://api.example.com/users/${id} HTTP ${response.status}: ${response.statusText} Observe que o bloco executa sempre, independente de sucesso ou erro. Isso é útil para limpeza de recursos. Quando você relança um erro com , ele

<h2>Entendendo Promises e seu Tratamento de Erros</h2>

<p>Uma Promise é a base do tratamento de erros assíncronos em JavaScript. Ela representa uma operação que pode estar pendente, resolvida ou rejeitada. Quando uma Promise é rejeitada, ela dispara um erro que precisa ser capturado. O método <code>.catch()</code> é a forma clássica de tratar esses erros, funcionando como um <code>try-catch</code> para operações assíncronos.</p>

<pre><code class="language-javascript">function buscarDados(url) {

return fetch(url)

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

.catch(erro =&gt; {

console.error(&#039;Erro ao buscar dados:&#039;, erro.message);

return { dados: [] };

});

}

buscarDados(&#039;https://api.example.com/users&#039;)

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

.catch(erro =&gt; console.error(&#039;Erro final:&#039;, erro));</code></pre>

<p>Neste exemplo, o primeiro <code>.catch()</code> captura erros da requisição ou parsing JSON. Se houver um erro não tratado, o segundo <code>.catch()</code> será acionado. É importante entender que cada <code>.catch()</code> ou <code>.then()</code> cria uma nova Promise, permitindo encadeamento seguro.</p>

<h2>Async/Await com Try-Catch</h2>

<p>O async/await é a sintaxe moderna e mais legível para trabalhar com Promises. Com ele, código assíncronos parece síncrono, e podemos usar <code>try-catch</code> tradicionais para tratar erros. Esta é a abordagem recomendada para novos projetos.</p>

<pre><code class="language-javascript">async function buscarUsuario(id) {

try {

const response = await fetch(https://api.example.com/users/${id});

if (!response.ok) {

throw new Error(HTTP ${response.status}: ${response.statusText});

}

const usuario = await response.json();

return usuario;

} catch (erro) {

console.error(&#039;Falha ao buscar usuário:&#039;, erro.message);

throw erro;

} finally {

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

}

}

// Uso

buscarUsuario(1)

.then(user =&gt; console.log(&#039;Usuário:&#039;, user))

.catch(erro =&gt; console.error(&#039;Não foi possível obter usuário&#039;));</code></pre>

<p>Observe que o bloco <code>finally</code> executa sempre, independente de sucesso ou erro. Isso é útil para limpeza de recursos. Quando você relança um erro com <code>throw</code>, ele propaga para a Promise retornada, permitindo tratamento em nível superior.</p>

<h3>Tratando Múltiplas Operações Assíncronas</h3>

<p>Quando você precisa executar várias operações em paralelo, <code>Promise.all()</code> é eficiente, mas rejeita se qualquer Promise falhar. <code>Promise.allSettled()</code> é mais seguro, pois aguarda todas as Promises e reporta o resultado de cada uma.</p>

<pre><code class="language-javascript">async function carregarDados() {

try {

const [usuarios, posts, comentarios] = await Promise.all([

fetch(&#039;https://api.example.com/users&#039;).then(r =&gt; r.json()),

fetch(&#039;https://api.example.com/posts&#039;).then(r =&gt; r.json()),

fetch(&#039;https://api.example.com/comments&#039;).then(r =&gt; r.json())

]);

return { usuarios, posts, comentarios };

} catch (erro) {

console.error(&#039;Uma ou mais requisições falharam:&#039;, erro);

return null;

}

}

// Versão com allSettled para mais controle

async function carregarDadosSeguro() {

const resultados = await Promise.allSettled([

fetch(&#039;https://api.example.com/users&#039;).then(r =&gt; r.json()),

fetch(&#039;https://api.example.com/posts&#039;).then(r =&gt; r.json()),

fetch(&#039;https://api.example.com/comments&#039;).then(r =&gt; r.json())

]);

resultados.forEach((resultado, idx) =&gt; {

if (resultado.status === &#039;rejected&#039;) {

console.error(Requisição ${idx} falhou:, resultado.reason);

}

});

return resultados;

}</code></pre>

<h2>Tratamento Avançado e Boas Práticas</h2>

<h3>Timeout e Retry com Exponential Backoff</h3>

<p>Em sistemas reais, você precisa lidar com timeouts e implementar retry com backoff exponencial para recuperação automática de falhas temporárias.</p>

<pre><code class="language-javascript">async function fetchComTimeout(url, timeout = 5000) {

const controller = new AbortController();

const timeoutId = setTimeout(() =&gt; controller.abort(), timeout);

try {

const response = await fetch(url, { signal: controller.signal });

clearTimeout(timeoutId);

return await response.json();

} catch (erro) {

clearTimeout(timeoutId);

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

throw new Error(&#039;Requisição expirou&#039;);

}

throw erro;

}

}

async function fetchComRetry(url, maxTentativas = 3, delayInicial = 1000) {

let ultimoErro;

for (let tentativa = 0; tentativa &lt; maxTentativas; tentativa++) {

try {

return await fetchComTimeout(url);

} catch (erro) {

ultimoErro = erro;

if (tentativa &lt; maxTentativas - 1) {

const delay = delayInicial * Math.pow(2, tentativa);

console.log(Tentativa ${tentativa + 1} falhou. Aguardando ${delay}ms...);

await new Promise(resolve =&gt; setTimeout(resolve, delay));

}

}

}

throw ultimoErro;

}

// Uso

fetchComRetry(&#039;https://api.example.com/dados&#039;)

.then(dados =&gt; console.log(&#039;Sucesso:&#039;, dados))

.catch(erro =&gt; console.error(&#039;Todas as tentativas falharam:&#039;, erro.message));</code></pre>

<h3>Tratamento Centralizado de Erros</h3>

<p>Em aplicações maiores, é bom centralizar a lógica de tratamento de erros usando middleware ou wrapper functions.</p>

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

static async executar(funcao, contexto = &#039;&#039;) {

try {

return await funcao();

} catch (erro) {

this.registrar(erro, contexto);

this.notificarUsuario(erro);

throw erro;

}

}

static registrar(erro, contexto) {

console.error([${contexto}] ${erro.message}, erro.stack);

// Enviar para serviço de logging

}

static notificarUsuario(erro) {

// Mostrar mensagem amigável ao usuário

const mensagem = erro.message.includes(&#039;timeout&#039;)

? &#039;A operação demorou muito. Tente novamente.&#039;

: &#039;Ocorreu um erro inesperado. Tente mais tarde.&#039;;

console.warn(&#039;Notificação:&#039;, mensagem);

}

}

// Uso

GerenciadorErros.executar(

() =&gt; fetchComRetry(&#039;https://api.example.com/dados&#039;),

&#039;Carregamento de Dados&#039;

)

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

.catch(() =&gt; {/ Já tratado /});</code></pre>

<h2>Conclusão</h2>

<p>O tratamento de erros assíncronos em JavaScript é fundamental para aplicações robustas. Três pontos-chave para lembrar: <strong>Primeiro</strong>, prefira async/await com try-catch para código mais legível e manutenível. <strong>Segundo</strong>, sempre implemente timeout e retry com backoff exponencial em requisições de rede, pois falhas temporárias são comuns. <strong>Terceiro</strong>, centralize sua lógica de tratamento de erros para evitar duplicação e facilitar manutenção — seus colegas e seu eu do futuro agradecerão.</p>

<h2>Referências</h2>

<ul>

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

<li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Promises" target="_blank" rel="noopener noreferrer">MDN Web Docs - Async/Await</a></li>

<li><a href="https://javascript.info/try-catch" target="_blank" rel="noopener noreferrer">JavaScript.info - Error handling, &quot;try...catch&quot;</a></li>

<li><a href="https://nodejs.org/en/docs/guides/error-handling/" target="_blank" rel="noopener noreferrer">Node.js Official Documentation - Error Handling</a></li>

<li><a href="https://github.com/getify/You-Dont-Know-JS" target="_blank" rel="noopener noreferrer">You Don&#039;t Know JS Yet - Async &amp; Performance</a></li>

</ul>

Comentários

Mais em JavaScript

HTTP Nativo em Node.js: Criando Servidores sem Framework na Prática
HTTP Nativo em Node.js: Criando Servidores sem Framework na Prática

Introdução ao HTTP Nativo em Node.js Node.js oferece o módulo nativo, permiti...

O que Todo Dev Deve Saber sobre React Router: Navegação, Rotas Dinâmicas e Proteção de Rotas
O que Todo Dev Deve Saber sobre React Router: Navegação, Rotas Dinâmicas e Proteção de Rotas

Introdução ao React Router React Router é a biblioteca padrão para navegação...

Testes em JavaScript: Jest, Vitest e Testing Library na Prática na Prática
Testes em JavaScript: Jest, Vitest e Testing Library na Prática na Prática

Introdução aos Frameworks de Teste em JavaScript Testes automatizados são a b...