Módulo 2 · Ciclo Comum · IN02 · Aula 8 de 11
Front-End II
Async, fetch() e Redes
Como uma página SSR vira interativa: JS no cliente conversa com endpoints JSON do back-end
⏱️ Event loop
🔮 Promises
⏳ async/await
🌐 fetch
🚫 AbortController
🔄 Estados UI
⏱️ Daily — 15 Minutos
Recap da aula 7: HTML, DOM, eventos. Quem terminou o formulário com validação?
15:00
✅ EJS funcionando 🌳 querySelector 📡 addEventListener 🚧 Impedimentos
📋 Agenda da Aula 8
Estrutura e objetivos da aula de hoje

🕐 Bloco 1 — Autoestudo (revisão e dúvidas)

Recap do material — Event Loop, Promises, async/await, fetch, headers e cookies.

🕑 Bloco 2 — Instrução (Professor)

Consumo de APIs com fetch, tratamento de erros, AbortController e estados de UI.

🕒 Bloco 3 — Almoço

Pausa para refeição antes da prática.

🎯 Bloco 4 — Desenvolvimento (Projeto)

Adicionar interatividade dinâmica às páginas SSR consumindo a própria API do projeto.

🗺️ Onde estamos no Módulo 2
Aula 8 de 11 — segunda aula de front-end, ligando UI ao back-end
1
Aulas 1–4 · Banco de dados
Modelagem, SQL, JOINs e relatórios
5
Aula 5 · Back-end + MVC + TypeScript
Setup, models, services, repositories
6
Aula 6 · Endpoints CRUD + zod
REST, validação, transações
7
Aula 7 · Front-End I
HTML semântico, EJS, DOM, eventos
8
Aula 8 · Front-End II ← VOCÊ ESTÁ AQUI
Async, fetch, AbortController, estados de UI
9
Aula 9 · Testes e Automação
Jest, supertest, Husky, CI
10
Aula 10 · CSS + JS avançado
Layouts modernos e UX
11
Aula 11 · Mergulhando nas Redes
TCP/IP, DNS, HTTPS — fundação

🎯 Objetivo da aula

Ao final, o aluno conecta a UI do projeto ao back-end via fetch(): lê a página renderizada com EJS e adiciona interações dinâmicas que chamam endpoints JSON, com tratamento de erro, cancelamento e estados de UI.

⏱️ JavaScript é single-threaded — então como é assíncrono?
Event loop, microtasks (Promises) e macrotasks (setTimeout, I/O)

🧠 Call stack

main() render() handleClick()

🟦 Microtask queue (Promises)

.then(callback) await resume

🟧 Macrotask queue (setTimeout, I/O, fetch resolve)

setTimeout cb DOM event

O ciclo

  • 1. JS executa o que está na call stack até esvaziar.
  • 2. Esvaziou? Drena todas as microtasks.
  • 3. Pega uma macrotask, coloca na stack, volta ao passo 1.

⚠️ Cuidado

Microtasks têm prioridade. Um while(true) dentro de um .then trava o event loop e congela a UI.

💡 Mental model

Promises = microtask. setTimeout = macrotask. Por isso Promise.resolve().then(...) roda antes de setTimeout(..., 0).

🔮 Promises — o contrato do "vai chegar depois"
Estados, métodos de instância e métodos estáticos
pending
Estado inicial. Ainda não resolveu nem rejeitou.
fulfilled
Resolveu com um valor. Dispara .then(value => ...).
rejected
Rejeitou com um erro. Dispara .catch(err => ...).
promises.js — métodos estáticos
// Espera todas resolverem (falha se uma falhar)
const [users, posts, tags] = await Promise.all([fetchUsers(), fetchPosts(), fetchTags()]);
// Espera todas, sem falhar — devolve { status, value | reason }
const results = await Promise.allSettled([api1(), api2(), api3()]);
// Primeira que resolver (ou rejeitar) ganha
const winner = await Promise.race([fetch(url), timeout(5000)]);

📜 .then / .catch / .finally

Encadeamento clássico. Cada .then retorna uma nova Promise. .finally roda em qualquer cenário (útil para fechar loaders).

⏳ async / await — açúcar sintático para Promises
Código assíncrono que se lê como síncrono
com Promises (.then)
function carregarUsuario(id) {
return fetch(`/api/users/${id}`)
.then(res => res.json())
.then(user => renderUser(user))
.catch(err => mostrarErro(err));
}
com async / await
async function carregarUsuario(id) {
try {
const res = await fetch(`/api/users/${id}`);
const user = await res.json();
renderUser(user);
} catch (err) { mostrarErro(err); }
}

🚫 Erro clássico

await só funciona dentro de função async. Top-level await existe em ES modules, mas dentro de funções comuns dispara SyntaxError.

✅ try/catch

Em async/await, erros viram exceções normais. Use try/catch exatamente como em código síncrono.

🌐 fetch() — GET é o caso default
Lendo dados da nossa API TypeScript+Express
public/js/users.js — GET /api/users
// fetch sem segundo argumento = GET
async function listarUsuarios() {
const res = await fetch('/api/users');
// res é Response — não é o JSON ainda!
if (!res.ok) throw new Error(`HTTP ${res.status}`);
// .json() retorna Promise — precisa de await
const data = await res.json();
return data;
}

🧱 Response

É um stream do corpo. Use .json(), .text(), .blob() conforme o tipo.

🔁 Reusável

Crie um helper getJSON(url) em public/js/api.js e reuse em todas as páginas.

📦 Mesmo origem

Como o front-end SSR e o JSON são servidos pelo mesmo Express, não há CORS — caminho relativo basta.

📮 fetch() — POST com JSON
Enviando dados para a API com headers e body
public/js/users.js — POST /api/users
async function criarUsuario(payload) {
const res = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!res.ok) {
const erro = await res.json();
throw new Error(erro.message ?? `HTTP ${res.status}`);
}
return await res.json();
}

📌 Três coisas obrigatórias no POST com JSON

  • method: 'POST' — sem isso, vira GET silenciosamente.
  • headers: { 'Content-Type': 'application/json' } — sem isso, o Express com express.json() não parseia o corpo.
  • body: JSON.stringify(...) — objeto JS não serializa sozinho. Sem stringify, o corpo vai como [object Object].
⚠️ Tratamento de erros — o pulo do gato
fetch só rejeita por erro de rede; 4xx/5xx chegam como sucesso

❌ Erro de rede (Promise rejeita)

  • DNS falhou (host não existe).
  • Sem internet.
  • CORS bloqueou.
  • Request foi cancelado.

→ Cai no catch.

📨 Erro HTTP (Promise resolve!)

  • 404 — recurso não existe.
  • 401 — não autenticado.
  • 422 — validação zod falhou.
  • 500 — bug do servidor.

res.ok = false, mas o await não dispara erro.

padrão correto: ler res.ok
async function getJSON(url) {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
}
🚫 AbortController — cancelando requests obsoletos
Busca digitada, troca de página, navegação rápida
public/js/search.js — typeahead com cancelamento
let controller = null;
input.addEventListener('input', async (e) => {
// Cancela request anterior se ainda estiver em voo
if (controller) controller.abort();
controller = new AbortController();
try {
const res = await fetch(`/api/search?q=${e.target.value}`, {
signal: controller.signal
});
renderResultados(await res.json());
} catch (err) {
if (err.name === 'AbortError') return; // silencioso
mostrarErro(err);
}
});

🎯 Quando usar

Sempre que um novo evento substitui o anterior: campo de busca digitando, abas trocando, paginação rápida, infinite scroll. Evita renderizar resultado antigo em cima do novo.

🔄 Loading · Empty · Error · Success
Toda UI assíncrona vive em 4 estados — não esqueça nenhum
Loading
Request em andamento. Mostre spinner ou skeleton.
<div>Carregando...</div>
📭
Empty
Resposta OK, mas data.length === 0. Mensagem amigável.
"Nenhum usuário cadastrado ainda."
⚠️
Error
Rede falhou ou status >=400. Mostrar erro + botão "tentar novamente".
"Falha ao carregar. Tentar novamente?"
Success
Renderiza o conteúdo final.
<ul>...</ul>
esqueleto canônico
setLoading();
try {
const data = await getJSON('/api/users');
if (data.length === 0) return renderEmpty();
renderList(data);
} catch (err) { renderError(err); }
🔁 Backend dual — HTML (EJS) + JSON na mesma rota lógica
O mesmo controller responde duas linguagens, reusando o service
GET /users
Renderiza views/users/index.ejs via SSR.
Carga inicial da página, SEO-friendly.
res.render('users/index', { users })
GET /api/users
Devolve JSON puro.
Consumido pelo JS do cliente após carga.
res.json({ data: users })
src/controllers/UserController.ts — dois métodos, um service
export class UserController {
constructor(private service: UserService) {}
// HTML — renderiza EJS
index = async (req, res) => {
const users = await this.service.listAll();
res.render('users/index', { users });
};
// JSON — mesma lógica, formato diferente
apiList = async (req, res) => {
const users = await this.service.listAll();
res.json({ data: users });
};
}
🛠️ Headers, cookies de sessão e Network tab
Inspecionando o que sai e o que entra na sua aplicação

📋 Headers comuns

Content-Type
Tipo do corpo enviado/recebido (application/json).
Accept
O que o cliente sabe consumir.
Authorization
Token bearer / Basic. Não usaremos hoje.
Cache-Control
Política de cache (browser e proxies).
Cookie / Set-Cookie
Sessão stateful — vai automático em mesma origem.

🍪 Cookies de sessão · SameSite

O navegador anexa o cookie de sessão automaticamente em fetch de mesma origem. SameSite=Lax é o default seguro: cookie envia em GETs cross-site mas não em POSTs.

🌐 CORS — em uma frase

Mesma origem (localhost:3000 servindo HTML e JSON)? Não há problema. Se um dia o front virar app.x.com e a API api.x.com, ative o middleware cors no Express.

🔍 DevTools · Network tab

F12 → Network. Veja URL, método, status, headers, payload e timing. É a primeira parada quando algo não funciona.

RM-ODP — As 5 Visões do Sistema
ISO/IEC 10746 · Interatividade dinâmica nas 5 visões
🏢 Enterprise
Experiência em tempo real
📋 Information
JSON consumido pelo cliente
⚙️ Computational
fetch, promises, UI states
🔧 Engineering
AbortController, retries
💻 Technology
Browser, fetch API

Esta aula: Visão Computational — adicionamos JS assíncrono no front, consumindo a API do back.

Esta Aula: JS Assíncrono
RM-ODP · RF, RNF e Artefato — 8 Eixos do JS assíncrono

📌 Requisito Funcional

Permitir que páginas atualizem dados sem reload, consumindo a API com tratamento adequado de erros e cancelamento.

📦 Artefato

  • 📡 Cliente fetch com async/await
  • 🛑 AbortController + estados (loading/erro)
  • 🔁 UI states alimentados por JSON

⚖️ RNF — 8 Eixos ISO/IEC 25010

USAB Usabilidade✅ Feedback imediato
CONF Confiabilidade✅ Erros tratados
DES Desempenho✅ Abort + paralelismo
SEG Segurança→ CORS + sameSite cookies
MANT Manutenibilidade→ Cliente API isolado
🎯 Recap + Entrega + Próxima aula
O que você precisa entregar até as 16h

✅ O que dominamos hoje

  • Event loop, microtasks vs macrotasks.
  • Promises e async/await com try/catch.
  • fetch GET e POST com JSON.
  • res.ok como checagem obrigatória.
  • AbortController para cancelamento.
  • Loading / Empty / Error / Success.
  • Backend dual HTML+JSON reusando service.

📦 Entrega da aula 8

MR feat(front): fetch e estados de UI: ao menos uma página EJS do projeto carrega lista via fetch('/api/...') com loading, empty e error states tratados.

🚀 Próximas aulas

  • Aula 9 · Testes e Automação — cada camada coberta pelo tipo certo de teste, Husky e CI.
  • Aula 10 · CSS + JS avançado — layouts modernos, animações, UX e padrões avançados de cliente.
  • Aula 11 · Redes — TCP/IP, DNS e HTTPS, fundação do que fizemos hoje.

🔮 Sneak peek aula 11

Quando você fez fetch('/api/users'), por baixo: DNS resolveu o host → TCP fez handshake → TLS criptografou → HTTP enviou request. Aula 11 abre cada caixa-preta dessas.

Módulo 2 · Ciclo Comum · Aula 8 de 11 — Front-End II
📋 Ponderada de Programação III
Autoestudo · Semana 07 · Fechamento documental do projeto integrado
🕐 11h às 12h 📍 Em sala ⚠️ Obrigatória 🏆 4 pontos 👤 Prof. Afonso Cesar Lelis Brandão 🧑‍🏫 Assist. Maria Eduarda

📝 Descrição

O projeto está integrado: backend rodando, frontend conectado, testes Jest escritos. Esta ponderada é o fechamento documental — você vai construir o RTM completo provando que cada persona tem suas necessidades atendidas por requisitos funcionais implementados, testados e evidenciados. O documento produzido aqui é a prova de que o sistema faz o que foi prometido.

🎯 Entrega mínima

  • RTM com ao menos 3 personas distintas e 5 RFs cobertos, sem células vazias na trilha principal
  • Tabela de RNF com os 8 eixos preenchidos, referenciando os RNFs definidos na Ponderada 1
  • Registro de mudanças de contrato (ao menos uma entrada, ou justificativa explícita de ausência)
  • Índice de evidências com localização de cada artefato referenciado no RTM

🧭 Como preencher

Preencha uma linha por combinação RF + RN. Não deixe células vazias na trilha principal, se não tem evidência, escreva "pendente" e justifique.

Mínimo: 3 personas distintas e 5 RFs cobertos.

Para cada eixo não funcional, descreva "como" o projeto atende ao RNF definido na Ponderada 1, com a evidência concreta.

Liste tudo que mudou em relação ao que foi modelado nas Ponderadas 1 e 2. Isso não é penalidade, é rastreabilidade real.

Liste cada evidência referenciada no RTM com uma descrição de onde encontrá-la

⏱ Ponderada 60:00