Pular para o conteúdo

A primeira escola especializada em tecnologia do Alto Solimões, com corpo técnico formado na área.

Módulo 5: JavaScript na página

Buscando dados com fetch

Trazer informação de fora sem recarregar a página

Buscar dados

async function carregarCursos() {
const response = await fetch("https://api.exemplo.com/cursos");
const cursos = await response.json();
console.log(cursos);
}

carregarCursos();
  • fetch faz a requisição e devolve uma promessa
  • await espera a promessa terminar
  • async marca a função como aquela que pode esperar

O que é uma promessa

Buscar dados leva tempo: décimos de segundo numa conexão boa, vários segundos numa ruim. Se o navegador ficasse parado esperando, a página inteira travaria: nem rolar daria.

Então fetch devolve na hora uma promessa: "vou te avisar quando chegar". await diz "espere aqui": e enquanto isso o navegador continua atendendo ao resto.

fetch não lança erro em 404

Esta é a armadilha:

// Errado: 404 e 500 passam batido
async function carregar() {
const response = await fetch("/cursos");
const dados = await response.json(); // estoura aqui, com erro confuso
return dados;
}

// Certo
async function carregar() {
const response = await fetch("/cursos");

if (!response.ok) {
throw new Error(`A API respondeu ${response.status}`);
}

return await response.json();
}

fetch só rejeita a promessa quando a rede falha: sem conexão, servidor inalcançável. Um 404 ou um 500 são respostas legítimas do ponto de vista dele, e chegam normalmente. Quem confere é você, com response.ok.

Tratar erro

async function carregarCursos() {
try {
const response = await fetch("/storefront/free-courses");

if (!response.ok) {
throw new Error(`A API respondeu ${response.status}`);
}

const dados = await response.json();
return dados.data;
} catch (erro) {
console.error("Falha ao carregar cursos:", erro);
return [];
}
}

try/catch pega os dois casos: a falha de rede que fetch rejeita e o erro que você mesmo lançou.

Os quatro estados

Toda tela que busca dado tem quatro:

<div id="area">
<p id="carregando" hidden>Carregando…</p>
<p id="erro" hidden role="alert"></p>
<p id="vazio" hidden>Nenhum curso disponível no momento.</p>
<ul id="cursos"></ul>
</div>
const elementos = {
carregando: document.querySelector("#carregando"),
erro: document.querySelector("#erro"),
vazio: document.querySelector("#vazio"),
lista: document.querySelector("#cursos"),
};

function mostrar(estado, mensagem = "") {
elementos.carregando.hidden = estado !== "carregando";
elementos.erro.hidden = estado !== "erro";
elementos.vazio.hidden = estado !== "vazio";
elementos.lista.hidden = estado !== "pronto";

if (estado === "erro") elementos.erro.textContent = mensagem;
}

async function carregar() {
mostrar("carregando");

try {
const response = await fetch("/storefront/free-courses");
if (!response.ok) throw new Error(`A API respondeu ${response.status}`);

const { data } = await response.json();

if (data.length === 0) {
mostrar("vazio");
return;
}

const fragmento = document.createDocumentFragment();

for (const curso of data) {
const item = document.createElement("li");
item.textContent = `${curso.name} - ${curso.lessonCount} aulas`;
fragmento.append(item);
}

elementos.lista.replaceChildren(fragmento);
mostrar("pronto");
} catch (erro) {
console.error(erro);
mostrar("erro", "Não foi possível carregar os cursos. Tente de novo.");
}
}

carregar();

O hidden é um atributo do HTML, e escondê-lo por ali: em vez de por style.display: mantém a decisão num lugar só.

role="alert" no parágrafo de erro faz o leitor de tela anunciar a mensagem assim que ela aparece.

Enviar dados

async function enviarInscricao(dados) {
const response = await fetch("/storefront/enrollments", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(dados),
});

if (!response.ok) {
const erro = await response.json();
throw new Error(erro.message ?? "Não foi possível enviar");
}

return await response.json();
}

O corpo vai como texto JSON, daí o JSON.stringify da aula 10, e o cabeçalho avisa o servidor do formato.

Várias buscas ao mesmo tempo

// Uma depois da outra: soma os tempos
const cursos = await carregarCursos();
const posts = await carregarPosts();

// Ao mesmo tempo: vale o mais lento
const [cursos2, posts2] = await Promise.all([carregarCursos(), carregarPosts()]);

Quando uma busca não depende da outra, Promise.all corta o tempo pela metade.

Exercícios

  1. Busque dados de uma API pública e mostre no console.
  2. Acrescente a verificação de response.ok.
  3. Envolva em try/catch e provoque um erro com um endereço inválido.
  4. Monte a tela com os quatro estados.
  5. Simule conexão lenta nas ferramentas do navegador (aba Network) e confira o estado de carregando.
  6. Faça duas buscas com Promise.all.
  7. Envie um POST com JSON.stringify no corpo.

Referências

Testa o que você leu

5 perguntas sobre esta aula. Errar aqui não custa nada - a explicação vem junto da correção.

Pergunta 1 de 5
O que `await` faz enquanto espera a resposta?