Programação Web 2

Fetch API / AJAX

Prof. Me. Danilo Bueno

O que é AJAX?

Asynchronous JavaScript And XML

Técnica que permite ao navegador comunicar-se com um servidor sem recarregar a página — atualizando apenas parte do conteúdo.

Hoje a troca de dados usa JSON (não mais XML), mas o nome permanece.

A ferramenta nativa dos navegadores modernos para isso é a Fetch API.

O problema do código síncrono

JavaScript executa uma linha por vez, esperando cada operação terminar.

console.log("1 — Buscando personagem...");

const personagem = buscarNaAPI(); // bloqueia tudo!

console.log("2 — Personagem recebido");

Enquanto buscarNaAPI() roda: o usuário não consegue clicar, digitar nem rolar a tela.

Assíncrono: reagir sem travar

Síncrono Assíncrono
Execução Uma por vez, em ordem Pode pular para a próxima tarefa
Bloqueio Sim — trava enquanto espera Não — continua executando
Exemplo Leitura de arquivo local Buscar dados em uma API

JavaScript é single-threaded — só faz uma coisa por vez.
Assincronicidade permite reagir a resultados futuros sem travar.

Como expressar assincronicidade?

A sintaxe evoluiu — veja o mesmo exemplo nas três formas

O que é uma Promise?

Objeto JavaScript que representa o resultado eventual de uma operação assíncrona — um valor que ainda não existe, mas existirá no futuro.

Estado Ocorre quando
pending A operação ainda está em andamento
fulfilled A operação concluiu com sucesso
rejected A operação falhou com erro

fetch() retorna uma Promise. Veremos três formas de trabalhar com ela.

1. Callback

buscarPersonagem(1, function(personagem) {
  console.log(personagem.name); // "Homer Simpson"
});

console.log("Isso roda antes do personagem chegar!");

Funciona — mas quando as operações se aninham vira Callback Hell:

buscarPersonagem(1, function(personagem) {
  buscarEpisodios(personagem, function(episodios) {
    buscarFrases(episodios, function(frases) {
      // Pyramid of Doom...
    });
  });
});

2. Promise + .then()

fetch("https://thesimpsonsapi.com/api/characters/1")
  .then(function(response) {
    return response.json();
  })
  .then(function(personagem) {
    console.log(personagem.name); // "Homer Simpson"
  })
  .catch(function(erro) {
    console.log("Erro:", erro.message);
  });

Encadeável, sem pirâmide. Cada .then() recebe o retorno do anterior.

3. async / await

async function buscarPersonagem(id) {
  const response = await fetch("https://thesimpsonsapi.com/api/characters/" + id);
  const personagem = await response.json();
  console.log(personagem.name); // "Homer Simpson"
}

buscarPersonagem(1);

Parece síncrono — mas é assíncrono.

  • async — declara que a função retorna uma Promise
  • await — pausa dentro da função até a Promise resolver

É exatamente essa sintaxe que vamos usar.

Como o fetch funciona por dentro?

Vamos construir buscarPersonagem passo a passo

fetch() retorna uma Promise

async function buscarPersonagem(id) {
  console.log(fetch("https://thesimpsonsapi.com/api/characters/" + id));
}

buscarPersonagem(1);
// → Promise { <pending> }

fetch() retorna imediatamente — mas os dados ainda não chegaram.
O objeto Promise { <pending> } representa um resultado futuro.

Por isso precisamos de await — para esperar esse resultado chegar.

O objeto Response

async function buscarPersonagem(id) {
  const response = await fetch("https://thesimpsonsapi.com/api/characters/" + id);

  console.log(response.status);     // 200
  console.log(response.ok);         // true (status 200–299)
}

O primeiro await entrega um objeto Response — não os dados ainda.
O corpo da resposta é um stream — precisa de um segundo passo para ler.

Por que dois awaits?

fetch(url)
    │
    ▼
Promise<Response>        ← await #1 — aguarda conexão e headers
    │
    ▼
Response  { status: 200, ok: true, ... }
    │
    ▼
response.json()
    │
    ▼
Promise<Object>          ← await #2 — aguarda leitura do body
    │
    ▼
{ id: 1, name: "Homer Simpson", occupation: "Safety Inspector" }

Função até aqui

async function buscarPersonagem(id) {
  const response = await fetch("https://thesimpsonsapi.com/api/characters/" + id);
  const personagem = await response.json();

  console.log(personagem.name);        // "Homer Simpson"
  console.log(personagem.occupation);  // "Safety Inspector"
}

Dois await — dois passos assíncronos.
personagem agora é o objeto JavaScript com todos os dados da API.

E quando algo dá errado?

try/catch — erros de rede

async function buscarPersonagem(id) {
  try {
    const response = await fetch("https://thesimpsonsapi.com/api/characters/" + id);
    const personagem = await response.json();
    console.log(personagem.name);
  } catch (erro) {
    // Captura: sem conexão, timeout, CORS bloqueado...
    console.log("Falha na requisição:", erro.message);
  }
}

catch captura erros de rede — quando o servidor não foi alcançado.

Mas e quando o servidor responde com 404 ou 500?

A armadilha do 404

async function buscarPersonagem(id) {
  try {
    const response = await fetch("https://thesimpsonsapi.com/api/characters/9999");

    console.log(response.status); // 404
    console.log(response.ok);     // false

    // O catch NÃO foi acionado — nenhuma exceção foi lançada!
    const personagem = await response.json(); // pode lançar SyntaxError...
  } catch (erro) { ... }
}

fetch não rejeita a Promise para erros HTTP.
Um 404 significa que a rede funcionou

Regra: sempre verifique response.ok manualmente.

Solução: checar response.ok

async function buscarPersonagem(id) {
  try {
    const response = await fetch("https://thesimpsonsapi.com/api/characters/" + id);

    if (!response.ok) {
      throw new Error("Personagem não encontrado (status: " + response.status + ")");
    }

    const personagem = await response.json();
    console.log(personagem.name);
  } catch (erro) {
    // Captura erros de rede E o throw acima
    console.log("Erro:", erro.message);
  }
}

throw dentro do try é capturado pelo mesmo catch.

finally — limpeza garantida

async function buscarPersonagem(id) {
  btnBuscar.disabled = true;  // desabilita antes de começar

  try {
    const response = await fetch("https://thesimpsonsapi.com/api/characters/" + id);
    if (!response.ok) throw new Error("Personagem não encontrado");
    const personagem = await response.json();
    exibirNaTela(personagem);
  } catch (erro) {
    exibirErro(erro.message);
  } finally {
    btnBuscar.disabled = false; // executa SEMPRE — sucesso ou falha
  }
}

finally garante que o botão nunca fica travado

Obrigado!

[email protected] · website/docs · github