Buscando dados com fetch
Buscar dados
async function carregarCursos() {
const response = await fetch("https://api.exemplo.com/cursos");
const cursos = await response.json();
console.log(cursos);
}
carregarCursos();
fetchfaz a requisição e devolve uma promessaawaitespera a promessa terminarasyncmarca 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
- Busque dados de uma API pública e mostre no console.
- Acrescente a verificação de
response.ok. - Envolva em
try/catche provoque um erro com um endereço inválido. - Monte a tela com os quatro estados.
- Simule conexão lenta nas ferramentas do navegador (aba Network) e confira o estado de carregando.
- Faça duas buscas com
Promise.all. - Envie um POST com
JSON.stringifyno corpo.
Referências
- Fetch - MDN - a introdução
- Promessas - MDN - o que são e como encadear
async/await- MDN - a forma legível de esperar
Testa o que você leu
5 perguntas sobre esta aula. Errar aqui não custa nada - a explicação vem junto da correção.