Identificação
- Duração: 2 horas
- Tipo: teoria aplicada e laboratório
- Entrega: cliente de API com estados completos
- Laboratório:
../exemplos/aula-2.7/index.html
Introdução
O que significa código síncrono?
No fluxo síncrono, cada instrução termina antes da seguinte:
const title = "Estudar";
const normalizedTitle = title.trim();
console.log(normalizedTitle);
Esse modelo é simples, mas algumas operações levam tempo, como solicitar dados pela rede, aguardar um temporizador ou consultar um banco de dados no servidor. Bloquear a interface durante a espera deixaria a página sem responder.
O que significa assíncrono?
Uma operação assíncrona pode começar agora e terminar depois. O JavaScript continua processando outras tarefas e recebe o resultado futuramente:
console.log("Antes");
setTimeout(() => {
console.log("Temporizador concluído");
}, 1000);
console.log("Depois");
Saída:
Antes
Depois
Temporizador concluído
Assíncrono significa executar tudo em paralelo?
Não. Assincronismo significa que uma espera não precisa bloquear o fluxo. Paralelismo significa realizar trabalho simultaneamente, normalmente com múltiplas threads, processos ou núcleos.
O navegador coordena rede e temporizadores, enquanto o JavaScript da interface normalmente executa uma tarefa por vez na thread principal.
O que é uma Promise?
Uma Promise representa o resultado futuro de uma operação. Ela possui três estados:
- pending: aguardando;
- fulfilled: concluída com valor;
- rejected: concluída com erro.
const promise = fetch("./data/tasks.json");
promise ainda não é o conteúdo da resposta.
O que é async/await?
É uma sintaxe para consumir Promises com um fluxo legível:
async function loadTasks() {
const response = await fetch("./data/tasks.json");
const tasks = await response.json();
return tasks;
}
await pausa aquela função assíncrona, não o navegador inteiro.
O que é Fetch?
Fetch é uma API do navegador para requisições HTTP:
const response = await fetch("/api/tasks");
Uma resposta HTTP possui status, cabeçalhos e corpo.
fetch rejeita quando recebe 404 ou 500?
Normalmente, não. Um erro HTTP ainda gera um objeto Response. Fetch costuma
rejeitar por falha de rede, URL inválida ou cancelamento. Verifique:
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
Onde isso aparece no projeto?
O laboratório consumirá JSON pelo servidor local e representará carregamento, sucesso, vazio, erro HTTP, JSON inválido e cancelamento.
Objetivos
Ao final da aula, o aluno deverá conseguir:
- diferenciar fluxo síncrono e assíncrono;
- explicar os estados de uma Promise;
- usar
.then,.catche.finally; - criar funções
async; - utilizar
awaitetry...catch; - realizar requisições com Fetch;
- verificar
response.ok; - interpretar e validar JSON;
- representar carregamento, sucesso, vazio e erro;
- cancelar operações com
AbortController; - diferenciar execução sequencial e concorrente;
- impedir respostas obsoletas de atualizar a página.
Pré-requisitos
- Aulas 2.1 a 2.6 concluídas;
- funções, módulos, objetos e JSON;
- tratamento de erros;
- projeto aberto por
http://localhost.
Pergunta orientadora
Como aguardar dados externos sem bloquear a página e sem deixar a interface inconsistente quando algo falha?
Roteiro sugerido
| Etapa | Duração |
|---|---|
| Modelo assíncrono e Promises | 25 min |
async/await e erros |
25 min |
| Fetch e HTTP | 30 min |
| Estados, cancelamento e concorrência | 20 min |
| Laboratório | 15 min |
| Revisão | 5 min |
1. Callbacks assíncronos
setTimeout(() => {
console.log("Executado depois");
}, 1000);
O callback é chamado quando o temporizador está pronto e a pilha de execução permite. Callbacks continuam importantes em eventos, mas Promises facilitam compor sequências assíncronas.
2. Criando uma Promise
const waitOneSecond = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Concluído");
}, 1000);
});
resolvecumpre a Promise com valor;rejectrejeita com erro.
Na maior parte da aplicação, consumiremos Promises oferecidas por APIs em vez de criá-las manualmente.
3. .then, .catch e .finally
fetch("./data/tasks.json")
.then((response) => response.json())
.then((tasks) => renderTasks(tasks))
.catch((error) => renderError(error))
.finally(() => setLoading(false));
Cada .then retorna uma nova Promise. .catch trata rejeições anteriores e
.finally executa após sucesso ou falha.
4. Funções async
Uma função async sempre retorna uma Promise:
async function getValue() {
return 42;
}
O valor vira uma Promise cumprida. Se a função lançar um erro, a Promise será rejeitada.
5. await
async function loadTasks() {
const response = await fetch("./data/tasks.json");
return response.json();
}
await entrega o valor quando a Promise é cumprida ou lança o motivo da rejeição:
try {
const tasks = await loadTasks();
renderTasks(tasks);
} catch (error) {
renderError(error);
}
6. Esquecer await
const tasks = loadTasks();
console.log(tasks.length);
tasks é uma Promise. Use:
const tasks = await loadTasks();
console.log(tasks.length);
7. Capturar rejeições
Este try não captura uma rejeição futura não aguardada:
try {
loadTasks();
} catch (error) {
}
Use await:
try {
await loadTasks();
} catch (error) {
}
Ou .catch(...).
8. Requisição com Fetch
const response = await fetch("./data/tasks.json", {
method: "GET",
headers: {
Accept: "application/json",
},
});
Para enviar JSON:
await fetch("/api/tasks", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(task),
});
9. O objeto Response
Propriedades úteis:
response.ok;
response.status;
response.statusText;
response.headers;
response.url;
ok é verdadeiro para status entre 200 e 299.
10. O corpo da resposta
const data = await response.json();
O corpo normalmente só pode ser consumido uma vez. Outros métodos incluem:
text();blob();arrayBuffer();formData().
Escolha conforme o tipo de resposta.
11. JSON inválido
Status 200 não garante JSON válido:
try {
const data = await response.json();
} catch (error) {
throw new InvalidResponseError(
"A resposta não contém JSON válido.",
{ cause: error },
);
}
Depois, valide a estrutura:
if (!Array.isArray(data)) {
throw new InvalidResponseError(
"A resposta deveria conter um array.",
);
}
12. Estado da interface
Uma operação assíncrona deve representar:
idle → loading → success
→ empty
→ error
→ cancelled
Para cada estado, defina:
- mensagem;
- conteúdo visível;
- controles disponíveis;
- ação para tentar novamente.
13. Carregamento
function setLoading(isLoading) {
loadButton.disabled = isLoading;
cancelButton.disabled = !isLoading;
}
Inclua texto acessível, não apenas animação.
14. Estado vazio
Uma lista vazia pode ser resposta válida:
if (tasks.length === 0) {
renderEmptyState();
return;
}
Não apresente “erro” quando o resultado correto é “nenhum item”.
15. Erro HTTP personalizado
export class HttpError extends Error {
constructor(message, status) {
super(message);
this.name = "HttpError";
this.status = status;
}
}
Uso:
if (!response.ok) {
throw new HttpError(
`HTTP ${response.status}`,
response.status,
);
}
16. Cancelamento com AbortController
const controller = new AbortController();
fetch(url, {
signal: controller.signal,
});
controller.abort();
O cancelamento normalmente produz AbortError:
if (error.name === "AbortError") {
showCancelled();
return;
}
É útil quando uma nova busca substitui a anterior ou o usuário abandona a operação.
17. Timeout
Podemos cancelar após um limite:
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
}, 5000);
try {
return await fetch(url, {
signal: controller.signal,
});
} finally {
clearTimeout(timeoutId);
}
18. Sequencial e concorrente
Sequencial:
const tasks = await loadTasks();
const users = await loadUsers();
Concorrente:
const [tasks, users] = await Promise.all([
loadTasks(),
loadUsers(),
]);
Use concorrência quando as operações são independentes.
Promise.all rejeita quando uma falha. Promise.allSettled entrega todos os
resultados para análise individual.
19. Respostas obsoletas
Se duas buscas forem iniciadas, a primeira pode terminar por último e substituir a mais recente. Estratégias:
- cancelar a operação anterior;
- associar um identificador à operação;
- confirmar que a resposta ainda é a atual antes de renderizar.
O laboratório cancela a operação anterior ao iniciar outra.
20. CORS
CORS é uma política do navegador para requisições entre origens. Origem combina protocolo, host e porta.
Quando uma API não permite a origem da página, a correção costuma estar na configuração da API, não em desativar a segurança do navegador.
O laboratório usa a mesma origem local.
21. Segurança
- não coloque chaves secretas no front-end;
- valide todo JSON recebido;
- use
textContentpara dados externos; - utilize HTTPS em produção;
- não registre tokens;
- trate autenticação no servidor;
- apresente mensagem segura ao usuário.
22. Estrutura do laboratório
assets/
api.js
errors.js
main.js
styles.css
data/
tasks.json
empty.json
invalid.json
api.js conhece HTTP. main.js conhece DOM e estados. errors.js define tipos de
falha.
23. Laboratório guiado
Abra o cliente de API.
Etapa 1 — Sucesso
Selecione Dados disponíveis e carregue. Observe Fetch, status HTTP, JSON e renderização.
Etapa 2 — Vazio
Selecione Lista vazia. O estado deve ser vazio, não erro.
Etapa 3 — HTTP 404
Selecione Recurso inexistente. response.ok deve detectar a falha.
Etapa 4 — JSON inválido
Selecione JSON inválido. O HTTP será 200, mas response.json() falhará.
Etapa 5 — Cancelamento
Selecione Resposta lenta, inicie e clique em Cancelar. A interface deve permitir nova tentativa.
24. Erros comuns
Esquecer await
O código tenta usar a Promise como valor final.
Não verificar response.ok
Um 404 segue pelo fluxo de sucesso e gera mensagem confusa.
Chamar todo erro de “sem internet”
Validação, JSON inválido, 404 e cancelamento possuem causas diferentes.
Não usar finally
O botão pode permanecer desabilitado depois de uma falha.
Expor segredo no navegador
Qualquer valor enviado ao front-end pode ser inspecionado.
Renderizar resposta antiga
Cancele ou identifique operações substituídas.
25. Boas práticas
- modele todos os estados;
- verifique status HTTP;
- valide o formato do JSON;
- use erros específicos;
- cancele operações obsoletas;
- restaure controles em
finally; - separe HTTP do DOM;
- ofereça nova tentativa;
- teste sucesso, vazio, falha e cancelamento;
- não dependa de serviço externo para exercícios essenciais.
26. Exercícios
Exercício 1 — Espera
Crie wait(milliseconds) que retorne uma Promise.
Exercício 2 — Fetch
Carregue um JSON local, verifique response.ok e confirme que é array.
Exercício 3 — Estados
Implemente idle, loading, success, empty e error.
Exercício 4 — Concorrência
Compare duas esperas sequenciais com Promise.all.
Exercício 5 — Cancelamento
Cancele a requisição anterior ao iniciar uma nova.
27. Desafio
Amplie o laboratório:
- adicione botão para tentar novamente;
- configure timeout;
- apresente duração da operação;
- carregue dois recursos com
Promise.all; - use
Promise.allSettledpara resultado parcial; - impeça resposta obsoleta de atualizar o DOM.
28. Lista de verificação de conclusão
- Sei explicar síncrono e assíncrono.
- Conheço os estados de uma Promise.
- Sei usar
.then,.catche.finally. - Consigo usar
async/await. - Verifico
response.ok. - Trato JSON inválido.
- Diferencio vazio e erro.
- Restaurei a interface em
finally. - Consigo cancelar com
AbortController. - Não exponho segredos no navegador.
29. Critérios de avaliação
| Critério | Pontos |
|---|---|
Uso correto de Promise e await |
20 |
| Verificação HTTP e JSON | 20 |
| Estados completos da interface | 25 |
| Cancelamento e respostas obsoletas | 15 |
| Separação entre API e DOM | 10 |
| Acessibilidade e segurança | 10 |
| Total | 100 |
Resumo
Nesta aula, aprendemos que:
- Promises representam resultados futuros;
async/awaitorganiza seu consumo;- Fetch não rejeita automaticamente erros HTTP;
- um corpo pode ser inválido mesmo após HTTP 200;
- interfaces precisam representar carregamento, sucesso, vazio, erro e cancelamento;
- operações independentes podem ser concorrentes;
- requisições obsoletas devem ser canceladas ou ignoradas.
Próxima aula
Na Aula 2.8, reuniremos o módulo em um painel final com armazenamento de preferências, testes de regras, importação e exportação, documentação e apresentação.