Tutorial Completo JavaScript ⚡

Dominando
Async em JavaScript

De setTimeout a Promise.all — entenda programação assíncrona de verdade com exemplos práticos e boas práticas.

10 Módulos
3 Estados Promise
ES2017+ Versão JS
Promise then / catch async / await try / catch Promise.all Promise.race allSettled
01

⏳ Programação assíncrona

Em JavaScript, algumas tarefas demoram para terminar. O JS não "trava" esperando — ele continua executando e resolve depois.

🌐

Buscar dados de uma API

📂

Ler arquivo do sistema

⏱️

Timers e delays

🗄️

Consultas a banco de dados

código síncrono — executa em ordem
console.log("1");
console.log("2");
console.log("3");
// 1, 2, 3
código assíncrono — setTimeout
console.log("Início");

setTimeout(() => {
  console.log("Demorou...");
}, 2000);

console.log("Fim");
saída — a ordem surpreende!
Início
Fim
Demorou...   ← aparece depois de 2 segundos
🧠
O JavaScript tem uma event loop: tarefas assíncronas vão para uma fila e são executadas assim que o código principal termina. Por isso "Fim" aparece antes de "Demorou...".
02

🔹 O que é Promise?

Uma Promise (promessa) é um objeto que representa o resultado de uma operação assíncrona — algo que ainda não aconteceu, mas vai acontecer no futuro.

estrutura básica
const minhaPromise = new Promise((resolve, reject) => {
  // tarefa assíncrona aqui
  // resolve(valor)  → sucesso
  // reject(erro)    → falha
});
exemplo simples
const promessa = new Promise((resolve, reject) => {
  const sucesso = true;

  if (sucesso) {
    resolve("Deu certo! ✔");
  } else {
    reject("Deu erro! ✘");
  }
});
consumindo com .then e .catch
promessa
  .then(res => console.log(res))   // "Deu certo! ✔"
  .catch(err => console.log(err)); // caso rejeite
exemplo real — simulando requisição de API
function buscarUsuario() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ nome: "Igor", idade: 25 });
    }, 2000);
  });
}

buscarUsuario().then(usuario => {
  console.log(usuario);
  // { nome: "Igor", idade: 25 }
});
03

🔹 Estados de uma Promise

Toda Promise passa por um destes 3 estados — e só pode mudar de pending para um dos outros dois:

pending
esperando
fulfilled
deu certo
|
rejected
deu erro
Estado Significado Trigger
pending Ainda aguardando resultado Estado inicial
fulfilled Operação completada com sucesso resolve() chamado
rejected Operação falhou reject() chamado
📌
Uma Promise imutável após resolvida — uma vez que vai para fulfilled ou rejected, não pode voltar para pending nem mudar de estado.
04

🔹 then / catch / finally

Três métodos para consumir o resultado de uma Promise:

.then(cb)

Executado quando a Promise é resolvida (fulfilled).

.catch(cb)

Executado quando a Promise é rejeitada (rejected).

.finally(cb)

Executado sempre, independente do resultado.

usando os três métodos
minhaPromise
  .then(resultado => {
    console.log("Sucesso:", resultado);
  })
  .catch(erro => {
    console.log("Erro:", erro);
  })
  .finally(() => {
    console.log("Sempre executa!");
  });
💡
Use .finally() para limpar estados de loading na UI — ele roda tanto no sucesso quanto no erro.
05

🔹 Encadeamento de Promises

Cada .then() retorna uma nova Promise, permitindo encadear operações assíncronas em sequência — eliminando o famoso "callback hell".

callback hell — o problema antigo
// ❌ difícil de ler e manter
fazerA(() => {
  fazerB(() => {
    fazerC(() => {
      fazerD(() => {
        console.log("fim"); // pirâmide da morte
      });
    });
  });
});
encadeamento com promises — solução
// ✅ linear e legível
fetchDados()
  .then(dados => processar(dados))
  .then(result => salvar(result))
  .then(final => console.log(final))
  .catch(err => console.log("Erro:", err));
exemplo prático encadeado
function dobrar(n) {
  return Promise.resolve(n * 2);
}

Promise.resolve(5)
  .then(dobrar)   // 10
  .then(dobrar)   // 20
  .then(dobrar)   // 40
  .then(v => console.log(v)); // 40
06

🔹 async / await

O async/await é açúcar sintático sobre Promises — torna o código assíncrono mais legível, parecendo código síncrono normal.

async function

Marca uma função como assíncrona. Sempre retorna uma Promise.

await

Pausa a execução da função até a Promise resolver. Só funciona dentro de async.

async — função assíncrona
async function minhaFuncao() {
  return "Olá";
}

// equivale a:
function minhaFuncao() {
  return Promise.resolve("Olá");
}
await — aguarda resultado
async function executar() {
  const resultado = await Promise.resolve("Deu certo!");
  console.log(resultado); // "Deu certo!"
}

executar();
exemplo com delay real
function esperar(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function processo() {
  console.log("Início");

  await esperar(2000);
  console.log("Depois de 2s");

  await esperar(1000);
  console.log("Depois de mais 1s");

  console.log("Fim");
}

processo();
saída
Início
Depois de 2s     ← após 2 segundos
Depois de mais 1s ← após mais 1 segundo
Fim
07

🔹 try / catch com async/await

Com async/await, trate erros com a estrutura try/catch — muito mais legível que encadear .catch().

tratamento de erro correto
async function buscar() {
  try {
    const resposta = await fetch("https://api.exemplo.com/dados");
    const dados = await resposta.json();
    console.log(dados);
  } catch (erro) {
    console.log("Erro:", erro);
  } finally {
    console.log("Requisição finalizada.");
  }
}
Bloco Quando executa
try { } Código que pode lançar erro
catch (e) { } Quando um erro ocorre no try
finally { } Sempre, independente de erro ou sucesso
⚠️
Nunca use await fora de uma função async — causa erro de sintaxe. A exceção é o top-level await em módulos ES2022+.
08

🔹 Promise.all, race e allSettled

Métodos estáticos para lidar com múltiplas Promises ao mesmo tempo:

Promise.all — aguarda todas (paralelismo)
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);

Promise.all([p1, p2, p3]).then(res => {
  console.log(res); // [1, 2, 3]
});
Promise.all com async/await
async function executar() {
  const [user, posts, comments] = await Promise.all([
    fetchUser(),
    fetchPosts(),
    fetchComments()
  ]);

  console.log(user, posts, comments);
}
Promise.race — primeira que termina
Promise.race([
  new Promise(r => setTimeout(() => r("A"), 1000)),
  new Promise(r => setTimeout(() => r("B"), 500))
]).then(console.log);

// "B"  (terminou primeiro)
Promise.allSettled — todas, com ou sem erro
Promise.allSettled([
  Promise.resolve("ok"),
  Promise.reject("erro"),
  Promise.resolve("ok também")
]).then(console.log);

// [
//   { status: "fulfilled", value: "ok" },
//   { status: "rejected",  reason: "erro" },
//   { status: "fulfilled", value: "ok também" }
// ]
Método O que faz Se uma falhar
Promise.all Aguarda todas terminarem Rejeita imediatamente
Promise.race Retorna a primeira que terminar Rejeita se a primeira falhar
Promise.allSettled Aguarda todas, mostra status individual Nunca rejeita — sempre retorna tudo
Promise.any Retorna a primeira que resolver Ignora rejeições
Use Promise.all para paralelizar requisições independentes — executa tudo ao mesmo tempo e espera o mais lento, muito mais rápido que await sequencial.
09

🔹 Boas práticas e erros comuns

async/await > .then()

Prefira async/await — mais legível e menos aninhado.

try/catch sempre

Sempre trate erros em funções async — sem catch silencioso.

Promise.all para paralelo

Não use await em série quando as tarefas são independentes.

Não misture estilos

Evite misturar .then() com await no mesmo bloco.

❌ erros comuns
// ❌ esqueceu o await — dados é uma Promise, não o resultado!
const dados = fetchDados();
console.log(dados); // Promise { <pending> }

// ❌ sem tratamento de erro
async function f() {
  const dados = await fetchDados(); // pode explodir silenciosamente
}

// ❌ await em série desnecessário (lento!)
const user  = await fetchUser();   // espera 1s
const posts = await fetchPosts();  // espera mais 1s
// total: 2s! Use Promise.all (seria 1s)
✅ boas práticas
// ✅ sempre await quando precisar do valor
const dados = await fetchDados();

// ✅ sempre try/catch
try {
  const dados = await fetchDados();
} catch (err) {
  console.log(err);
}

// ✅ Promise.all para operações independentes
const [user, posts] = await Promise.all([
  fetchUser(),
  fetchPosts()
]);
10

🔹 Exemplo final completo

Um cenário realista: buscar usuário, buscar posts em paralelo e exibir tudo com tratamento de erro:

exemplo completo — realista
// Simula requisições assíncronas
function buscarUsuario() {
  return new Promise(resolve => {
    setTimeout(() => resolve({ id: 1, nome: "Igor" }), 1500);
  });
}

function buscarPosts(userId) {
  return new Promise(resolve => {
    setTimeout(() => resolve(["Post 1", "Post 2"]), 1000);
  });
}

// Função principal
async function main() {
  try {
    console.log("Buscando dados...");

    // Busca usuário e posts em paralelo!
    const [usuario, posts] = await Promise.all([
      buscarUsuario(),
      buscarPosts(1)
    ]);

    console.log("Usuário:", usuario.nome);
    console.log("Posts:", posts);

  } catch (err) {
    console.log("Erro:", err);
  } finally {
    console.log("Finalizado!");
  }
}

main();
saída
Buscando dados...
Usuário: Igor          ← após ~1.5s (paralelo, não 2.5s!)
Posts: ["Post 1", "Post 2"]
Finalizado!
resumo mental
// Promise     → objeto que resolve no futuro
// then/catch  → forma "clássica" de consumir
// async       → função que retorna Promise
// await       → pausa e aguarda a Promise
// try/catch   → trata erros com async/await
// Promise.all → paralelo — mais rápido!
// Promise.race → vence o primeiro
// allSettled  → retorna todos, com status
🚀
Caminho recomendado: entenda setTimeout → crie Promises → use then/catch → migre para async/await → domine Promise.all → integre com fetch em APIs reais.