Se você já se sentiu perdido no labirinto de callbacks e promises do JavaScript, prepare-se! **Async Await JavaScript** veio para simplificar sua vida, tornando o código assíncrono mais legível e fácil de manter. Imagine poder escrever código assíncrono que se parece com código síncrono – é essa a mágica do Async Await!
Promises: A Base do Async Await
O que são Promises?

No mundo JavaScript, uma Promise representa um valor que pode ainda não estar disponível no momento da criação. Pense nela como um “compromisso” de que, em algum momento, você terá um resultado – seja ele um valor, ou um erro. As Promises são cruciais para lidar com operações assíncronas, como chamadas de API ou leitura de arquivos.
As Promises surgiram como uma alternativa elegante aos tradicionais callbacks, que frequentemente resultavam em um código complexo e difícil de gerenciar, conhecido como “callback hell”.
Estados de uma Promise

- Pending (Pendente): O estado inicial, onde a operação ainda está em andamento.
- Fulfilled (Realizada): A operação foi concluída com sucesso, e um valor está disponível.
- Rejected (Rejeitada): A operação falhou, e um motivo para a falha está disponível.
Como criar uma Promise

Criar uma Promise é simples. Veja um exemplo:
const minhaPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const sucesso = true; // Simula uma operação bem-sucedida
if (sucesso) {
resolve("Operação realizada com sucesso!");
} else {
reject("Operação falhou!");
}
}, 2000);
});
Consumindo Promises
Usando .then() e .catch()

Para obter o resultado de uma Promise, usamos os métodos .then() (para sucesso) e .catch() (para falhas). Veja:
minhaPromise
.then(resultado => {
console.log("Resultado:", resultado);
})
.catch(erro => {
console.error("Erro:", erro);
});
Encadeamento de Promises

Promises podem ser encadeadas, permitindo que você execute várias operações assíncronas em sequência. Isso é feito retornando uma nova Promise dentro do .then().
const buscaUsuario = () => {
return new Promise(resolve => {
setTimeout(() => resolve({ id: 1, nome: 'João' }), 1000);
});
};
const buscaPosts = (usuarioId) => {
return new Promise(resolve => {
setTimeout(() => resolve([{ id: 1, titulo: 'Post 1' }]), 1000);
});
};
buscaUsuario()
.then(usuario => {
console.log("Usuário:", usuario);
return buscaPosts(usuario.id);
})
.then(posts => {
console.log("Posts:", posts);
})
.catch(erro => {
console.error("Erro:", erro);
});
Async: Simplificando a Assincronidade
O que é a palavra-chave async?

A palavra-chave async é usada para declarar uma função assíncrona. Funções assíncronas permitem que você use a palavra-chave await dentro delas.
Como declarar uma função assíncrona

É simples! Basta adicionar async antes da declaração da função:
async function minhaFuncaoAsync() {
// Código assíncrono aqui
}
O valor de retorno de uma função async
Uma função async sempre retorna uma Promise, mesmo que você não especifique explicitamente. Se a função retornar um valor diretamente, ele será automaticamente envolvido em uma Promise resolvida.
Await: Esperando a Resolução da Promise
O que é a palavra-chave await?
A palavra-chave await é usada para pausar a execução de uma função async até que uma Promise seja resolvida ou rejeitada. Isso permite que você escreva código assíncrono que se parece com código síncrono.
Usando await dentro de funções async
await só pode ser usado dentro de funções async. Ele “espera” a Promise ser resolvida e retorna o resultado. Se a Promise for rejeitada, uma exceção será lançada.
async function obterDados() {
const resposta = await fetch('https://exemplo.com/api/dados');
const dados = await resposta.json();
return dados;
}
Como await simplifica o código assíncrono
Com await, você evita o aninhamento excessivo de .then() e .catch(), tornando o código mais legível e fácil de entender. Imagine a diferença entre usar várias chamadas .then() aninhadas e simplesmente aguardar o resultado com await! É como comparar um emaranhado de fios com um cabo organizado.
Async Await em Ação: Exemplos Práticos
Exemplo 1: Fetch de Dados de uma API
Código com Promises
fetch('https://api.github.com/users/takenet')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Erro:', error));
Código com Async Await (Comparação)
async function buscarDados() {
try {
const response = await fetch('https://api.github.com/users/takenet');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Erro:', error);
}
}
buscarDados();
Tratamento de Erros com try...catch
No exemplo acima, usamos try...catch para lidar com erros de forma elegante. Se a chamada fetch falhar, o bloco catch será executado.
Exemplo 2: Múltiplas Requisições Assíncronas
Execução em Série vs. Paralela
Quando você precisa fazer várias requisições assíncronas, pode optar por executá-las em série (uma após a outra) ou em paralelo (simultaneamente). A escolha depende do seu caso de uso.
Usando Promise.all() com Async Await
Para executar requisições em paralelo, você pode usar Promise.all(). Ele recebe um array de Promises e retorna uma nova Promise que é resolvida quando todas as Promises do array são resolvidas. Ele é especialmente útil quando você precisa coletar dados de várias APIs antes de prosseguir.
async function buscarDadosMultiplos() {
try {
const [usuario, posts] = await Promise.all([
fetch('https://api.exemplo.com/usuario/1').then(res => res.json()),
fetch('https://api.exemplo.com/posts?usuarioId=1').then(res => res.json())
]);
console.log('Usuário:', usuario);
console.log('Posts:', posts);
} catch (error) {
console.error('Erro:', error);
}
}
buscarDadosMultiplos();
Exemplo 3: Timeout com Async Await
Implementando um Timeout para Promises
Às vezes, você quer garantir que uma Promise não demore muito para ser resolvida. Para isso, você pode implementar um timeout.
function timeout(ms, promise) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error('Timeout'));
}, ms);
promise.then(
resolve,
reject
).finally(() => clearTimeout(timer));
})
}
async function buscarDadosComTimeout() {
try {
const data = await timeout(5000, fetch('https://api.exemplo.com/dados').then(res => res.json()));
console.log('Dados:', data);
} catch (error) {
console.error('Erro:', error);
}
}
buscarDadosComTimeout();
Cancelando Promises com AbortController
Para ter mais controle sobre as Promises, você pode usar o AbortController. Ele permite cancelar uma requisição fetch em andamento.
const controller = new AbortController();
const signal = controller.signal;
async function buscarDadosCancelavel() {
try {
const response = await fetch('https://api.exemplo.com/dados', { signal });
const data = await response.json();
console.log('Dados:', data);
} catch (error) {
console.error('Erro:', error);
}
}
buscarDadosCancelavel();
// Para cancelar a requisição:
// controller.abort();
Tratamento de Erros Eficiente
Try…Catch: O Bloco Essencial
Envolvendo código await em blocos try...catch
Sempre que usar await, envolva o código em um bloco try...catch para capturar erros que possam ocorrer. Isso evita que sua aplicação quebre inesperadamente.
Lidando com exceções assíncronas
Dentro do bloco catch, você pode tratar a exceção de diversas formas: exibir uma mensagem de erro para o usuário, registrar o erro em um log, ou tentar recuperar a operação.
Melhores Práticas de Tratamento de Erros
Log de erros detalhado
Registrar informações detalhadas sobre o erro (como o tipo do erro, a mensagem, e o stack trace) pode ser crucial para depurar e corrigir problemas.
Retornando mensagens de erro amigáveis
Em vez de exibir mensagens de erro técnicas e confusas para o usuário, retorne mensagens amigáveis e fáceis de entender. Isso melhora a experiência do usuário e evita frustrações. Para o log, seja o mais detalhado possível, mas para o usuário, simplifique a mensagem.
Async Await vs. Promises: Qual Escolher?
Vantagens do Async Await
Legibilidade aprimorada
O código com async await é mais fácil de ler e entender do que o código com Promises, especialmente quando você tem várias operações assíncronas em sequência.
Código mais conciso
async await permite escrever código mais curto e direto, evitando o aninhamento excessivo de .then() e .catch().
Debug mais fácil
Depurar código com async await é mais fácil, pois você pode usar os recursos de debugging tradicionais (como breakpoints) de forma mais eficaz.
Desvantagens do Async Await
Requer conhecimento de Promises
Para usar async await de forma eficaz, você precisa entender como as Promises funcionam. Mas, ei, você já está aprendendo isso aqui!
Pode levar a código bloqueante se não usado corretamente
Se você não usar async await corretamente, pode acabar bloqueando a execução do código, especialmente em operações que demoram muito. Por isso, é importante entender como o Event Loop do JavaScript funciona.
Quando usar Async Await e quando usar Promises
Recomendação:
- Async Await: Use sempre que possível para tornar o código mais legível e fácil de manter.
- Promises: Use em casos onde você precisa de mais controle sobre a execução da Promise, como cancelar uma requisição ou lidar com vários resultados.
A escolha entre Async Await e Promises depende do seu caso de uso e da sua preferência pessoal. No entanto, na maioria dos casos, Async Await é a melhor opção por ser mais fácil de ler e manter.
Dicas e Truques Avançados
IIFE (Immediately Invoked Function Expression) com Async Await
Usando Async Await em escopos globais
Às vezes, você precisa usar async await em um escopo global (fora de uma função). Para isso, você pode usar uma IIFE (Immediately Invoked Function Expression).
(async () => {
const dados = await fetch('https://api.exemplo.com/dados').then(res => res.json());
console.log('Dados:', dados);
})();
Async Generators e Async Iterators
Processamento assíncrono de streams de dados
Async Generators e Async Iterators são usados para processar streams de dados de forma assíncrona. Eles permitem que você pause e retome a execução de uma função durante o processamento de dados.
Debugging Async Await
Ferramentas e técnicas para depurar código assíncrono
Para depurar código com async await, você pode usar as mesmas ferramentas e técnicas que usa para depurar código síncrono, como breakpoints, console.log e debuggers. Além disso, algumas IDEs oferecem recursos específicos para depurar código assíncrono.
O Chrome DevTools, por exemplo, é uma ferramenta poderosa para depurar código JavaScript, incluindo código com async await. Ele permite que você inspecione o estado das variáveis, execute o código passo a passo, e defina breakpoints para pausar a execução em pontos específicos.
Dúvidas Frequentes
Async Await funciona em todos os navegadores?
Sim! A grande maioria dos navegadores modernos suporta Async Await. Mas, para garantir compatibilidade com navegadores mais antigos, você pode usar um transpiler como o Babel.
Qual a diferença entre Async Await e Promises?
Promises são a base do Async Await. Async Await é uma forma mais elegante e legível de trabalhar com Promises.
Posso usar Async Await fora de uma função async?
Não. A palavra-chave await só pode ser usada dentro de uma função declarada com a palavra-chave async.
Como lidar com erros em Async Await?
Use blocos try...catch para envolver o código await e capturar erros que possam ocorrer.
Async Await bloqueia a execução do código?
Não, o Event Loop do JavaScript continua funcionando normalmente. O await apenas pausa a execução da função async até que a Promise seja resolvida.
Para não esquecer:
Async Await é uma ferramenta poderosa para simplificar o código assíncrono em JavaScript. Use-o com sabedoria e combine-o com um bom tratamento de erros para criar aplicações robustas e fáceis de manter!
E aí, preparado para dominar o Async Await? Compartilhe suas dúvidas e experiências nos comentários! Se este guia te ajudou, compartilhe com seus amigos e colegas!




