Quando utilizar Promises ou Async Await?

⏳ Para argumentar essa questão vou trazer para o contexto de uma aplicação mobile/web construída com React.

Introdução


Essas duas funcionalidades são utilizadas para lidar com programação assíncrona no Javascript.

Promise é um objeto usado para processamento assíncrono. Um Promise (de "promessa") representa um valor que pode estar disponível agora, no futuro ou nunca.
O async/await é uma nova forma de tratar Promises dentro do nosso código, evitando a criação de cascatas de .then

Temos um post feito pelo Diego Fernandes, onde ele explica cada uma dessas features, vale muito a pena a leitura: Promises | Async/Await. 🚀

Then() // Então, você já sabe, mas vamos refletir!
Se você já leu esses dois artigos já sabe a resposta, mas quero deixar aqui alguns argumentos, reflexões e construir isso junto com vocês. 🙂

Contexto

O código abaixo quando é executado renderiza o componente CreatePoint, que quando montado em tela o useEffect é executado, ele irá buscar os items (id, title, image_url) na API (backend da aplicação).

Se tudo der certo, esses dados serão preenchidos no useState items, e por fim será renderizado na tela, que é o retorno da função, confira:

import React, { useEffect, useState } from "react";
import api from "../../services/api";

interface Item {
  id: number;
  title: string;
  image_url: string;
}

const CreatePoint: React.FC = () => {
 const [items, setItems] = useState<Item[]>([]);

  // com Promises
  useEffect(() => {
    api
      .get("/items")
      .then((response) => {
        setItems(response.data);
      })
      .catch((error) => {
        alert("Ocorreu um erro ao buscar os items");
      });
  }, []);

  // com Async Await
  useEffect(() => {
    async function getItems() {
      try {
        const { data } = await api.get("/items");
        setItems(data);
      } catch (error) {
        alert("Ocorreu um erro ao buscar os items");
      }
    }
    getItems();
  }, []);

  return (
    <fieldset>
      <legend>
        <h2>Ítens de Coleta</h2>
        <span>Selecione um ou mais ítens abaixo</span>
      </legend>

      <ul className="items-grid">
        {items.map((item: Item) => (
          <li key={item.id} onClick={() => {}}>
            <img src={item.image_url} alt={item.title} />
            <span>{item.title}</span>
          </li>
        ))}
      </ul>
    </fieldset>
  );
};

export default CreatePoint;

Esse exemplo faz parte de um dos trechos de código da NLW.

Claro que eu precisava apenas de um useEffect para isso acontecer, no código está com os dois apenas para efeito didático e comparação.

Nesse contexto a melhor maneira de escrever esse fluxo na minha opinião é usando apenas o Promises().then().catch(), ou seja da primeira forma.

Por que?

  • Evitou criar uma função a mais;
  • No hook useEffect não podemos colocar async/await, precisamos criar uma função e chamar de dentro do useEffect, ou poderia criar a função fora do useEffect mas chamá-la de dentro dele;
  • Com a Promises já lido com o sucesso no then() e com falha diretamente no catch(), não preciso criar uma complexidade de try/catch, ela já fica encapsulada na Promises;
  • Usei a resposta da API para colocar dentro de um state, não fiz mais nada complexo.

Breve comparativo

Faria sentido usar o async/await quando precisasse fazer mais de uma chamada na API, ou alguma verificação mais complexa, ou usasse os dados da resposta em uma outra situação.

Exemplo de utilizar Async/Await:

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);
  });
}

Como groups depende de user, é melhor usar async/await para melhorar a legibilidade e não ter o callback hell.

Mesmo exemplo porém usando promises SEM a sintaxe 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);
      });
    });
  });
});

Veja que esse encadeamento deixa o código menos legível.

Performance?

Nada muda, quando o código for transpilado ou compilado (se estiver usando typescript) ele será otimizado pelo webpack + babel, e ficará quase que ilegível pelo programador, então nesse quesito no contexto que apresentei, tanto faz qual utilizar.

Conclusão

Eu curto muitoooo o Async/Await, mas nem só por isso eu vou ficar utilizando ele toda hora, ainda mais no useEffect, dentro do contexto que apresentei.

Se você trabalha em um time, veja o padrão de código que o pessoal utiliza e também aguarde um Code Review na sua Pull Request.

E ai o que achou do post? Faz sentido para você? Quais seus argumentos? Vamos atualizar esse post juntos?

Recomende 💜 e compartilhe 📣  o post.  Até o próximo!

O conhecimento é continuo, e sempre vai ter um próximo nível! 💡