# Aula 2.7 - Promises, async/await e Fetch

## 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`](../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:

```js
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:

```js
console.log("Antes");

setTimeout(() => {
  console.log("Temporizador concluído");
}, 1000);

console.log("Depois");
```

Saída:

```text
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.

```js
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:

```js
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:

```js
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:

```js
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:

1. diferenciar fluxo síncrono e assíncrono;
2. explicar os estados de uma Promise;
3. usar `.then`, `.catch` e `.finally`;
4. criar funções `async`;
5. utilizar `await` e `try...catch`;
6. realizar requisições com Fetch;
7. verificar `response.ok`;
8. interpretar e validar JSON;
9. representar carregamento, sucesso, vazio e erro;
10. cancelar operações com `AbortController`;
11. diferenciar execução sequencial e concorrente;
12. 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

```js
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

```js
const waitOneSecond = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Concluído");
  }, 1000);
});
```

- `resolve` cumpre a Promise com valor;
- `reject` rejeita com erro.

Na maior parte da aplicação, consumiremos Promises oferecidas por APIs em vez de
criá-las manualmente.

## 3. `.then`, `.catch` e `.finally`

```js
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:

```js
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`

```js
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:

```js
try {
  const tasks = await loadTasks();
  renderTasks(tasks);
} catch (error) {
  renderError(error);
}
```

## 6. Esquecer `await`

```js
const tasks = loadTasks();
console.log(tasks.length);
```

`tasks` é uma Promise. Use:

```js
const tasks = await loadTasks();
console.log(tasks.length);
```

## 7. Capturar rejeições

Este `try` não captura uma rejeição futura não aguardada:

```js
try {
  loadTasks();
} catch (error) {
}
```

Use `await`:

```js
try {
  await loadTasks();
} catch (error) {
}
```

Ou `.catch(...)`.

## 8. Requisição com Fetch

```js
const response = await fetch("./data/tasks.json", {
  method: "GET",
  headers: {
    Accept: "application/json",
  },
});
```

Para enviar JSON:

```js
await fetch("/api/tasks", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Accept: "application/json",
  },
  body: JSON.stringify(task),
});
```

## 9. O objeto `Response`

Propriedades úteis:

```js
response.ok;
response.status;
response.statusText;
response.headers;
response.url;
```

`ok` é verdadeiro para status entre 200 e 299.

## 10. O corpo da resposta

```js
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:

```js
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:

```js
if (!Array.isArray(data)) {
  throw new InvalidResponseError(
    "A resposta deveria conter um array.",
  );
}
```

## 12. Estado da interface

Uma operação assíncrona deve representar:

```text
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

```js
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:

```js
if (tasks.length === 0) {
  renderEmptyState();
  return;
}
```

Não apresente “erro” quando o resultado correto é “nenhum item”.

## 15. Erro HTTP personalizado

```js
export class HttpError extends Error {
  constructor(message, status) {
    super(message);
    this.name = "HttpError";
    this.status = status;
  }
}
```

Uso:

```js
if (!response.ok) {
  throw new HttpError(
    `HTTP ${response.status}`,
    response.status,
  );
}
```

## 16. Cancelamento com `AbortController`

```js
const controller = new AbortController();

fetch(url, {
  signal: controller.signal,
});

controller.abort();
```

O cancelamento normalmente produz `AbortError`:

```js
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:

```js
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:

```js
const tasks = await loadTasks();
const users = await loadUsers();
```

Concorrente:

```js
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 `textContent` para 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

```text
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](../exemplos/aula-2.7/index.html).

### 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.allSettled` para 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`, `.catch` e `.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/await` organiza 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.
