kb.erickguedes.com
JavaScript: Fundamentos para Web

Programação Assíncrona

Aula 6 de 8

Callbacks

// setTimeout
setTimeout(() => {
  console.log('Executado após 2 segundos');
}, 2000);

// setInterval
const intervalo = setInterval(() => {
  console.log('A cada 1 segundo');
}, 1000);

// Parar intervalo
setTimeout(() => clearInterval(intervalo), 5000);

// Callback Hell (evitar!)
function buscarUsuario(id, callback) {
  setTimeout(() => {
    callback({ id, nome: 'João' });
  }, 1000);
}

function buscarPedidos(usuarioId, callback) {
  setTimeout(() => {
    callback([{ id: 1, total: 150 }, { id: 2, total: 80 }]);
  }, 1000);
}

buscarUsuario(1, (usuario) => {
  buscarPedidos(usuario.id, (pedidos) => {
    // callback hell - difícil de ler e manter
    console.log(usuario, pedidos);
  });
});

Promises

// Criar Promise
const promessa = new Promise((resolve, reject) => {
  const sucesso = true;

  setTimeout(() => {
    if (sucesso) {
      resolve('Operação concluída!');
    } else {
      reject(new Error('Algo deu errado'));
    }
  }, 1000);
});

// Consumir Promise
promessa
  .then(resultado => console.log(resultado))
  .catch(erro => console.error(erro))
  .finally(() => console.log('Finalizado'));

Refatorando Callbacks

function buscarUsuario(id) {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ id, nome: 'João' }), 1000);
  });
}

function buscarPedidos(usuarioId) {
  return new Promise((resolve) => {
    setTimeout(() => resolve([
      { id: 1, total: 150 },
      { id: 2, total: 80 }
    ]), 1000);
  });
}

// Promise chain
buscarUsuario(1)
  .then(usuario => buscarPedidos(usuario.id))
  .then(pedidos => console.log(pedidos))
  .catch(erro => console.error(erro));

Métodos Estáticos

// Promise.all - aguarda TODAS (falha rápida)
const p1 = fetch('/api/usuario');
const p2 = fetch('/api/produtos');
const p3 = fetch('/api/config');

Promise.all([p1, p2, p3])
  .then(respostas => Promise.all(respostas.map(r => r.json())))
  .then(([usuario, produtos, config]) => {
    console.log('Tudo carregado:', usuario, produtos, config);
  });

// Promise.allSettled - aguarda TODAS (sem falha rápida)
Promise.allSettled([p1, p2, p3])
  .then(resultados => {
    resultados.forEach(r => {
      if (r.status === 'fulfilled') console.log('OK:', r.value);
      if (r.status === 'rejected') console.log('FALHOU:', r.reason);
    });
  });

// Promise.race - primeira a resolver/rejeitar vence
Promise.race([
  fetch('/api/dados'),
  new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), 5000)
  )
]);

// Promise.any - primeira a resolver (ignora rejeições)
Promise.any([buscarUsuario(1), buscarUsuario(2)])
  .then(primeiro => console.log('Primeiro a responder:', primeiro));

Async/Await

async function carregarDados() {
  try {
    const usuario = await buscarUsuario(1);
    const pedidos = await buscarPedidos(usuario.id);
    console.log(usuario, pedidos);
  } catch (erro) {
    console.error('Erro:', erro);
  }
}

carregarDados();

// Execução paralela com async/await
async function carregarParalelo() {
  try {
    const [usuario, produtos] = await Promise.all([
      buscarUsuario(1),
      fetch('/api/produtos').then(r => r.json())
    ]);
    return { usuario, produtos };
  } catch (erro) {
    console.error('Falha ao carregar:', erro);
    throw erro;
  }
}

// Top-level await (módulos)
// const dados = await fetch('/api/config').then(r => r.json());

Exemplo Prático com fetch

async function buscarDadosGitHub(usuario) {
  const url = `https://api.github.com/users/${usuario}`;

  try {
    const response = await fetch(url);

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }

    const data = await response.json();
    console.log(`Usuário: ${data.login}`);
    console.log(`Repositórios públicos: ${data.public_repos}`);
    return data;
  } catch (erro) {
    console.error('Falha na requisição:', erro.message);
    throw erro;
  }
}

// Opções da requisição
async function enviarDados(url, dados) {
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer token123'
    },
    body: JSON.stringify(dados)
  });

  if (!response.ok) throw new Error('Erro na requisição');
  return response.json();
}

Lab: Buscador de CEP

Crie uma função assíncrona que busca endereço por CEP usando a API ViaCEP. Trate erros e timeout.

node buscar-cep.js
async function buscarCEP(cep) {
  const cepLimpo = cep.replace(/\D/g, '');
  if (cepLimpo.length !== 8) throw new Error('CEP inválido');

  const url = `https://viacep.com.br/ws/${cepLimpo}/json/`;

  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 5000);

  try {
    const response = await fetch(url, { signal: controller.signal });
    const data = await response.json();

    if (data.erro) throw new Error('CEP não encontrado');

    console.log(`Logradouro: ${data.logradouro}`);
    console.log(`Bairro: ${data.bairro}`);
    console.log(`Cidade: ${data.localidade}/${data.uf}`);
    return data;
  } catch (erro) {
    if (erro.name === 'AbortError') throw new Error('Timeout na requisição');
    throw erro;
  } finally {
    clearTimeout(timeout);
  }
}

buscarCEP('01310-100').catch(console.error);

Async/await tornou o código assíncrono legível como síncrono. Use Promise.all() para paralelismo e try/catch sempre.