Javascript assíncrono: async/await

React 20 de Dez de 2017

Esse é o segundo post da série Javascript Assíncrono, no post passado falamos sobre Promises.

Agora que você já sabe trabalhar com Promises no Javascript, é preciso ir para o próximo nível. Nesse post vamos falar sobre a nova maravilhosa feature do ECMAScript 2017 (ES8) já aprovada e presente nativamente e no Babel.

O async/await é uma nova forma de tratar Promises dentro do nosso código, evitando a criação de cascatas de .then como vimos no post passado. Vale lembrar que continuamos trabalhando com as Promises, mas elas ficam menos visíveis e verbosas.

Sintaxe

Utilizamos o prefixo async antes de definir uma função para indicar que estamos tratando de um código assíncrono, e com o prefixo adicionado, podemos utilizar o await antes das Promises indicando um ponto a ser aguardado pelo código. Vamos entender como isso funciona na prática:

// Modelo antigo (ES6)
function fetchUser(user) {
  api.get(`/users/${user}`).then(response => {
    console.log(response);
  });
}

// Novo modelo (ES8)
async function fetchUser(user) {
  const response = await api.get(`/users/${user}`);
  console.log(response);
}

Veja como o código ficou mais limpo, não precisamos mais declarar os .then ou .catch e ter medo de alguma Promise não executar antes de utilizarmos seu resultado pois o await faz todo papel de aguardar com que a requisição retorne seu resultado.

Acho que o maior problema que o async/await resolve é a famosa cascata de Promises que falei no post passado. Imaginemos o seguinte código montado com ES6 (sem async/await):

api.get('/users/diego3g').then(user => {
  api.get(`/groups/${user.id}`).then(groups => {
    groups.map(group => {
      api.get(`/group/${group.id}`).then(groupInfo => {
        console.log(groupInfo);
      });
    })
  });
});

Esse emaranhado de códigos e .then que vemos pode ser facilmente melhorado utilizando essa nova sintaxe:

async function fetchUserAndGroups() {
  const user = await api.get('/users/diego3g');
  const groups = await api.get(`/groups/${user.id}`);

  groups.map(group => {
    const groupInfo = await api.get(`/group/${group.id}`);
    console.log(groupInfo);
  });
}

Cada vez que definimos um await estamos indicando para o nosso código aguardar a Promise seguinte executar e retornar um resultado para seguir adiante, dessa forma evitamos que as próximas linhas executem sem as variáveis necessárias.


Vale lembrar que toda função que definimos com async automaticamente se torna uma Promise, isto é, podemos anexar funções assíncronas uma às outras dessa forma, vamos ver como isso fica no código:

async function fetchUser() {
  const response = await api.get('/users/diego3g');
  
  return response;
}

async function fetchGroups() {
  const user = await fetchUser();
  
  const response = await api.get(`/groups/${user.id}`);
  
  console.log(response);
}

E o .catch?

Até agora vimos como enviar o resultado do antigo .then para nossa variável utilizando o await, mas como interceptar erros? Com essa nova sintaxe, podemos utilizar o bom e velho try/catch para garantir que os erros do nosso código e das respostas das Promises não deixem rastros ao usuário final. Vamos ver como funciona essa nova sintaxe:

// Sintaxe antiga (ES6)

function fetchUser() {
  api.get('/users/diego3g')
    .then(response => console.log(response));
    .catch(err => console.log('Erro:', err));
}

// Nova sintaxe (ES8)

async function fetchUser() {
  try {
    const response = await api.get('/users/diego3g');
    
    console.log(response);
  } catch (err) {
    console.log('Erro:', err);
  }
}

Além de deixar o código mais bonito, uma ótima utilidade é utilizarmos um único catch para várias Promises, assim, se os erros dessas Promises tiverem o mesmo resultado para o usuário final, podemos tratá-los uma só vez (podendo ainda separar em vários try/catch caso os erros sejam diferentes).

Quando utilizar?

Uma pergunta difícil de responder, mas na minha opinião, não existe contraindicação para utilizar o async/await, na verdade faz tempo que não escrevo um .then porque acredito que a nova sintaxe deixa o código muito mais limpo.

Se você trabalha com React ou bibliotecas baseadas em classes pode também adicionar o async nos métodos da classe:

class Component {
  async componentDidMount() {
    // ... await api.get(...);
  }
}

Concluindo

Com a vinda da nova sintaxe do async/await no ES8 o código se tornou mais limpo, evitando a necessidade de criar cascatas de .then e .catchque estávamos acostumados até então.

Não esquece de deixar suas palmas e um comentário dizendo o que achou do post 🙂

Marcadores

Diego Fernandes

Programador full-stack, apaixonado pelas melhores tecnologias de desenvolvimento back-end, front-end e mobile, é co-fundador e CTO na Rocketseat.