<h2>O Problema das Callbacks e Promises</h2>
<p>Antes de entender async/await, você precisa conhecer o contexto. JavaScript executa código de forma assíncrona naturalmente — operações como requisições HTTP, leitura de arquivos e timers não bloqueiam a execução. Historicamente, usávamos callbacks para lidar com essas operações, mas isso levava ao famoso "callback hell": código aninhado, difícil de ler e manter. As Promises vieram como solução, permitindo uma sintaxe mais limpa com <code>.then()</code> e <code>.catch()</code>, mas ainda não era intuitivo como código síncrono.</p>
<pre><code class="language-javascript">// Callback Hell (difícil de ler)
fetchUser(userId, function(err, user) {
if (err) console.error(err);
fetchPosts(user.id, function(err, posts) {
if (err) console.error(err);
fetchComments(posts[0].id, function(err, comments) {
console.log(comments);
});
});
});
// Com Promises (melhor, mas ainda verboso)
fetchUser(userId)
.then(user => fetchPosts(user.id))
.then(posts => fetchComments(posts[0].id))
.then(comments => console.log(comments))
.catch(err => console.error(err));</code></pre>
<h2>Entendendo Async/Await</h2>
<p>Async/await é açúcar sintático sobre Promises que permite escrever código assíncrono com aparência síncrona. Uma função <code>async</code> sempre retorna uma Promise, e a palavra-chave <code>await</code> pausa a execução até que uma Promise seja resolvida. Isso torna o fluxo de controle muito mais legível e intuitivo para quem está acostumado com programação síncrona tradicional.</p>
<pre><code class="language-javascript">// Agora a mesma lógica é clara e linear
async function processUserData(userId) {
try {
const user = await fetchUser(userId);
const posts = await fetchPosts(user.id);
const comments = await fetchComments(posts[0].id);
console.log(comments);
} catch (err) {
console.error(err);
}
}
// Chamar uma função async
processUserData(1);</code></pre>
<p>A função <code>fetchUser</code>, <code>fetchPosts</code> e <code>fetchComments</code> retornam Promises. O <code>await</code> pausa a execução até que cada uma seja resolvida, e o resultado é atribuído diretamente à variável. Se alguma Promise for rejeitada, o bloco <code>catch</code> intercepta o erro. Isso é muito mais legível do que encadear múltiplos <code>.then()</code>.</p>
<h2>Padrões Avançados e Armadilhas Comuns</h2>
<h3>Paralelismo vs. Sequência</h3>
<p>Um erro comum é usar <code>await</code> desnecessariamente em sequência quando operações podem rodar em paralelo. Se você tem duas requisições independentes, executá-las em série é ineficiente. Use <code>Promise.all()</code> para operações que não dependem uma da outra.</p>
<pre><code class="language-javascript"></code></pre>
<h3>Tratamento de Erros</h3>
<p>O <code>try/catch</code> é o padrão recomendado, mas você pode também encadear <code>.catch()</code> em uma Promise. Para erros em múltiplas operações paralelas, <code>Promise.all()</code> rejeita assim que uma falha, enquanto <code>Promise.allSettled()</code> espera todas terminarem (sucesso ou falha).</p>
<pre><code class="language-javascript">// Promise.allSettled aguarda todas as operações
async function tratarMultiplosErros() {
const resultados = await Promise.allSettled([
fetchUser(1),
fetchUser(2),
fetchUser(999) // vai falhar
]);
resultados.forEach((resultado, index) => {
if (resultado.status === 'fulfilled') {
console.log(User ${index}:, resultado.value);
} else {
console.log(User ${index} falhou:, resultado.reason);
}
});
}</code></pre>
<h3>Loops Assíncrono</h3>
<p>Quando você precisa iterar sobre um array e aguardar operações dentro do loop, cuidado: <code>forEach()</code> não aguarda promises. Use <code>for...of</code> ou <code>.reduce()</code> para controlar a sequência.</p>
<pre><code class="language-javascript"></code></pre>
<h3>Funções Auxiliares: Implementação Real</h3>
<pre><code class="language-javascript">// Simular requisições HTTP (para teste)
function fetchUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, name: User ${id} });
} else {
reject(new Error('ID inválido'));
}
}, 1000);
});
}
// Usar em uma aplicação real
async function main() {
try {
const user = await fetchUser(1);
console.log('Usuário encontrado:', user);
const multiplos = await Promise.all([
fetchUser(1),
fetchUser(2),
fetchUser(3)
]);
console.log('Múltiplos usuários:', multiplos);
} catch (err) {
console.error('Erro:', err.message);
}
}
main();</code></pre>
<h2>Conclusão</h2>
<p>Async/await revoluciona a forma como lidamos com código assíncrono em JavaScript, tornando-o tão legível quanto síncrono. <strong>Primeiro aprendizado:</strong> sempre prefira async/await sobre callbacks e evite o excesso de <code>.then()</code> — o código fica mais limpo e fácil de manter. <strong>Segundo aprendizado:</strong> lembre-se que <code>await</code> pausa a execução, então use <code>Promise.all()</code> quando operações são independentes para não desperdiçar performance. <strong>Terceiro aprendizado:</strong> dominando loops assíncrono e tratamento de erros com <code>try/catch</code>, você terá total controle sobre fluxos complexos de dados e requisições.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://developer.mozilla.org/pt-BR/docs/Learn_web_development/Extensions/Async_JS/Promises" target="_blank" rel="noopener noreferrer">MDN Web Docs - Async/await</a></li>
<li><a href="https://javascript.info/async-await" target="_blank" rel="noopener noreferrer">JavaScript.info - Async/await</a></li>
<li><a href="https://tc39.es/ecma262/#sec-async-function-definitions" target="_blank" rel="noopener noreferrer">ECMAScript Specification - Async Function</a></li>
<li><a href="https://nodejs.org/en/docs/guides/blocking-vs-non-blocking/" target="_blank" rel="noopener noreferrer">Node.js Official Docs - Asynchronous</a></li>
<li><a href="https://eloquentjavascript.net/11_async.html" target="_blank" rel="noopener noreferrer">Eloquent JavaScript - Promises</a></li>
</ul>