Promises e Async/Await em JavaScript

As Promises são uma das funcionalidades mais importantes do JavaScript moderno, permitindo o tratamento elegante de operações assíncronas.

O que são Promises?

Uma Promise é um objeto que representa a eventual conclusão (ou falha) de uma operação assíncrona e seu valor resultante.

promise-basica.jsjavascript
const minhaPromise = new Promise((resolve, reject) => {
  // Operação assíncrona
  setTimeout(() => {
    const sucesso = true;
    
    if (sucesso) {
      resolve("Operação concluída com sucesso!");
    } else {
      reject("Algo deu errado!");
    }
  }, 1000);
});

minhaPromise
  .then(resultado => console.log(resultado))
  .catch(erro => console.error(erro));

Estados de uma Promise

Uma Promise pode estar em um de três estados:

  • Pending: Estado inicial, nem fulfilled nem rejected
  • Fulfilled: A operação foi concluída com sucesso
  • Rejected: A operação falhou
Dica
Uma vez que uma Promise é resolvida (fulfilled ou rejected), ela não pode mudar de estado. Isso garante previsibilidade no código.

Async/Await - Sintaxe Moderna

O async/await é uma sintaxe mais limpa para trabalhar com Promises, tornando o código assíncrono mais legível.

async-await.jsjavascript
async function buscarDados() {
  try {
    const resposta = await fetch('/api/dados');
    const dados = await resposta.json();
    
    console.log('Dados recebidos:', dados);
    return dados;
  } catch (erro) {
    console.error('Erro ao buscar dados:', erro);
    throw erro;
  }
}

// Uso da função
buscarDados()
  .then(dados => {
    // Processar dados
  })
  .catch(erro => {
    // Tratar erro
  });

Padrões Avançados

Promise.all() - Execução Paralela

Use Promise.all() quando precisar executar múltiplas operações assíncronas em paralelo e aguardar todas elas.

promise-all.jsjavascript
async function buscarTodosDados() {
  try {
    const [usuarios, posts, comentarios] = await Promise.all([
      fetch('/api/usuarios').then(r => r.json()),
      fetch('/api/posts').then(r => r.json()),
      fetch('/api/comentarios').then(r => r.json())
    ]);

    return { usuarios, posts, comentarios };
  } catch (erro) {
    // Se qualquer uma falhar, todas falham
    console.error('Erro ao buscar dados:', erro);
  }
}

Promise.allSettled() - Aguardar Todas

Diferente do Promise.all(), o allSettled() aguarda todas as promises terminarem, independente de sucesso ou falha.

promise-allsettled.jsjavascript
async function buscarDadosComFallback() {
  const resultados = await Promise.allSettled([
    fetch('/api/dados-primarios'),
    fetch('/api/dados-secundarios'),
    fetch('/api/dados-cache')
  ]);

  const sucessos = resultados
    .filter(resultado => resultado.status === 'fulfilled')
    .map(resultado => resultado.value);

  const falhas = resultados
    .filter(resultado => resultado.status === 'rejected')
    .map(resultado => resultado.reason);

  console.log('Sucessos:', sucessos.length);
  console.log('Falhas:', falhas.length);
}

Boas Práticas

  1. Sempre trate erros com try/catch ou .catch()
  2. Use async/await para código mais legível
  3. Evite callback hell usando Promises
  4. Use Promise.all() para operações paralelas
  5. Considere timeouts para operações que podem travar
timeout-promise.jsjavascript
function comTimeout(promise, ms) {
  return Promise.race([
    promise,
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Timeout')), ms)
    )
  ]);
}

// Uso
try {
  const dados = await comTimeout(
    fetch('/api/dados-lentos'),
    5000 // 5 segundos de timeout
  );
} catch (erro) {
  if (erro.message === 'Timeout') {
    console.log('Operação muito lenta, cancelada');
  }
}

Conclusão

Promises e async/await são fundamentais para o JavaScript moderno. Dominar esses conceitos é essencial para criar aplicações robustas e performáticas.