Entendendo async/await em JavaScript

Descubra como usar async/await em JavaScript, uma maneira moderna e mais legível de lidar com operações assíncronas.

O que é async/await e como funciona?

Em JavaScript, o uso de funções assíncronas é muito comum para realizar tarefas como requisições de rede ou manipulação de grandes volumes de dados sem bloquear a execução do código. Uma das maneiras mais modernas e eficientes de lidar com funções assíncronas é usando async e await. Eles são baseados em Promises e ajudam a tornar o código assíncrono mais legível e mais fácil de escrever. Neste tutorial, vamos entender o que são async e await, como usá-los e como eles podem simplificar o seu código.

O que é async?

A palavra-chave async é usada para declarar uma função como assíncrona, o que significa que ela sempre retornará uma Promise. Mesmo que você não use explicitamente uma Promise dentro da função, o JavaScript irá automaticamente envolver o valor retornado em uma Promise. Isso permite usar o await dentro da função para esperar pela resolução de uma Promise.

Exemplo básico de uma função assíncrona com async:

async function minhaFuncao() {
  return 'Resultado';
}

minhaFuncao().then(result => console.log(result));  // 'Resultado'

Neste exemplo, a função minhaFuncao é declarada como async, e ela retorna uma Promise que resolve o valor 'Resultado'. O uso do then() fora da função demonstra como a Promise é resolvida.

O que é await?

A palavra-chave await só pode ser usada dentro de funções async. Ela faz com que o JavaScript espere pela resolução de uma Promise antes de continuar a execução do código na linha seguinte. Isso faz com que o código assíncrono pareça síncrono, tornando-o muito mais legível.

Exemplo de uso do await para esperar a resolução de uma Promise:

async function minhaFuncao() {
  let resultado = await novaPromise();
  console.log(resultado);  // 'Operação concluída'
}

function novaPromise() {
  return new Promise(resolve => {
    setTimeout(() => resolve('Operação concluída'), 1000);
  });
}

Neste exemplo, a função novaPromise retorna uma Promise que é resolvida após 1 segundo. A palavra-chave await faz com que a execução de minhaFuncao seja pausada até que a Promise seja resolvida, e o resultado é então impresso no console.

Explicação do código acima

  • A função minhaFuncao é assíncrona e utiliza o await para esperar a resolução da Promise retornada por novaPromise.
  • O código dentro de minhaFuncao só prossegue quando a Promise é resolvida, o que torna o código muito mais fácil de entender em comparação com o uso de callbacks ou encadeamento de .then().

Como async/await melhora o código assíncrono

Um dos maiores benefícios do async/await é que ele permite escrever código assíncrono de uma maneira mais sequencial e parecida com o código síncrono. Isso torna o código muito mais fácil de ler, entender e depurar. Além disso, você pode combinar async/await com o uso de Promises para realizar múltiplas operações assíncronas em sequência ou em paralelo.

Exemplo de encadeamento de várias Promises com async/await:

async function executarOperacoes() {
  try {
    let resultado1 = await operacao1();
    let resultado2 = await operacao2(resultado1);
    console.log('Resultados:', resultado1, resultado2);
  } catch (error) {
    console.log('Erro:', error);
  }
}

async function operacao1() {
  return 'Primeiro resultado';
}

async function operacao2(result1) {
  return result1 + ' + Segundo resultado';
}

executarOperacoes();

Neste exemplo, as operações são realizadas uma após a outra, de forma simples e legível. O uso de try/catch permite o tratamento de erros de maneira centralizada, sem a necessidade de múltiplos manipuladores de erros.

Quando não usar await?

Embora o await seja muito útil, você deve ter cuidado ao usá-lo em loops ou em situações onde várias operações assíncronas precisam ser executadas em paralelo. Quando você usa await dentro de um loop, o JavaScript irá esperar a conclusão de cada operação antes de passar para a próxima, o que pode resultar em uma execução mais lenta do que o necessário.

Exemplo de código que pode ser otimizado:

async function processarDados() {
  let resultados = [];
  for (let i = 0; i < 5; i++) {
    resultados.push(await buscarDados(i));
  }
  console.log(resultados);
}

async function buscarDados(id) {
  return new Promise(resolve => setTimeout(() => resolve(id), 1000));
}

processarDados();

Aqui, o código está esperando que cada chamada à função buscarDados seja concluída antes de continuar para a próxima. Para otimizar isso, podemos usar Promise.all() para executar todas as operações em paralelo.

Usando Promise.all() com async/await

async function processarDados() {
  let resultados = await Promise.all([
    buscarDados(0),
    buscarDados(1),
    buscarDados(2),
    buscarDados(3),
    buscarDados(4)
  ]);
  console.log(resultados);
}

Com Promise.all(), todas as Promises são iniciadas ao mesmo tempo, e o código aguarda que todas sejam resolvidas antes de continuar. Isso melhora o desempenho, especialmente quando você está lidando com múltiplas operações independentes.

Conclusão

async/await é uma poderosa ferramenta em JavaScript que facilita o trabalho com código assíncrono. Ao usá-la, você pode escrever código mais legível e sequencial, sem a necessidade de encadeamentos complexos de Promises ou callbacks. O await permite que você "pausa" a execução até que uma Promise seja resolvida, enquanto o async transforma funções em Promises. Usar essas ferramentas de forma eficaz pode melhorar significativamente a qualidade e legibilidade do seu código.

O uso de async/await revoluciona o desenvolvimento com JavaScript, simplificando o código assíncrono e permitindo que ele se pareça com código síncrono. Isso não só melhora a legibilidade, mas também facilita o tratamento de erros e o encadeamento de operações. No entanto, é importante entender quando usar await e quando outras abordagens, como Promise.all(), podem ser mais eficientes.

Algumas aplicações:

  • Facilitar a escrita e o entendimento de código assíncrono em JavaScript.
  • Substituir o uso de callbacks aninhados, tornando o código mais legível e fácil de depurar.
  • Permitir que múltiplas operações assíncronas sejam executadas de forma sequencial e simples.
  • Utilizar com Promise.all() para executar várias Promises em paralelo, otimizando o desempenho do código.

Dicas para quem está começando

  • Comece com exemplos simples de async e await antes de passar para usos mais complexos.
  • Entenda o fluxo de execução de código assíncrono com async/await e como ele pode ser mais intuitivo do que o encadeamento de Promises.
  • Pratique o uso de try/catch para capturar erros em funções assíncronas e garantir que seu código seja robusto.
  • Evite usar await em loops quando várias operações puderem ser feitas em paralelo, use Promise.all() para otimizar.

Contribuições de Fernando Antunes

Compartilhe este tutorial: O que é async/await e como funciona?

Compartilhe este tutorial

Continue aprendendo:

Como encadear then() em Promises?

Entenda como usar o método `then()` em Promises para encadear várias operações assíncronas e criar fluxos de execução mais legíveis.

Tutorial anterior

Qual a diferença entre Promise.then() e async/await?

Entenda as principais diferenças entre `Promise.then()` e `async/await`, dois métodos populares para lidar com código assíncrono em JavaScript.

Próximo tutorial