Pular para o conteúdo
murad.guide

Referência Essencial da Web

Capítulo 22

JavaScript assíncrono

3 min de leitura 527 palavras 1 gravura

Trabalhe numa cópia da pasta inicio/ desta etapa. Ela já abre ou executa com as instruções do README. Leia o código, mude uma coisa e anote sua previsão antes de recarregar. Compare com a referência na pasta da etapa. Se nada mudar, confira a extensão do arquivo, o arquivo salvo e o caminho aberto. Blocos adicionais do capítulo são experiências separadas; a pasta de referência contém o programa completo desta etapa.

A Biblioteca da Ribeira, acervo comunitário fictício no bairro da Ribeira, Recife, lista títulos sem travar a página. O pedido vai ao arquivo livros.json. A resposta chega depois. Enquanto isso, a visitante ainda pode clicar.

O exemplo está em exemplos/22-javascript-assincrono/. Sirva a pasta em 127.0.0.1. O ciclo: ler, copiar, mudar uma coisa, prever, recarregar, conferir.

O que você vai aprender

Código que trava a página

JavaScript no navegador, neste volume, roda numa linha. Se uma tarefa demora e espera no mesmo lugar, a página congela. Clique não responde. Isso é bloqueio.

Buscar um arquivo pela rede demora. Por isso o pedido não espera “na frente” da visitante. Ele segue em paralelo. Quando a resposta chega, o código continua.

Promessa: espera, certo, erro

Uma promessa é um objeto que representa um resultado futuro. Três estados: esperando (pending), deu certo (fulfilled), falhou (rejected).

Promessa em espera, cumprida ou rejeitada

Você não “abre” a promessa na hora. Você diz o que fazer depois, se der certo, e o que fazer se falhar.

async/await e fetch

fetch pede um recurso e devolve uma promessa. async marca a função. await espera o resultado sem travar o restante da página do mesmo jeito que um laço infinito travaria.

<!DOCTYPE html>
<html lang="pt-BR">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Busca assíncrona</title>
  </head>
  <body>
    <h1>Buscar títulos</h1>
    <p>Sirva esta pasta em 127.0.0.1.</p>
    <button type="button" id="buscar">Buscar</button>
    <ul id="lista"></ul>
    <script src="busca.js"></script>
  </body>
</html>
document.getElementById("buscar").addEventListener("click", async function () {
  const lista = document.getElementById("lista");
  lista.textContent = "Buscando…";
  try {
    const resposta = await fetch("livros.json");
    const livros = await resposta.json();
    lista.innerHTML = "";
    livros.forEach(function (livro) {
      const item = document.createElement("li");
      item.textContent = livro.titulo;
      lista.appendChild(item);
    });
  } catch (erro) {
    lista.textContent = "Falhou. Use o servidor local.";
  }
});

livros.json nesta pasta:

[{"titulo":"Maré Alta"},{"titulo":"Caderno do Cais"}]

Na pasta principal do volume, que contém a subpasta curso:

python -m http.server 8765 --bind 127.0.0.1 --directory curso

Abra http://127.0.0.1:8765/exemplos/22-javascript-assincrono/. Clique em Buscar. Preveja o aviso “Buscando…” antes da lista. Confira.

Worker, só a ideia

Um worker é outro fio de execução. Serve para conta pesada sem congelar o desenho da página. Neste volume você não cria worker. Basta o nome e o motivo.

Erro comum: abrir index.html pelo disco (file://) e esperar o fetch. Muitos navegadores bloqueiam. Use 127.0.0.1. Não use 0.0.0.0.

Arquivo no disco é JSON e JS. A janela pede. O servidor local responde. Publicação é outro passo, que este capítulo não pede.

Atividades

  1. Por que a página mostra “Buscando…” antes dos títulos?
  2. O que muda se livros.json não existir?
  3. Worker resolve o mesmo problema que await fetch?

Respostas comentadas

  1. O clique dispara o pedido e segue. O texto avisa que a resposta ainda não chegou. Sem isso, a lista parece morta.
  2. fetch rejeita ou a leitura falha. O catch troca o aviso. A página não precisa travar.
  3. Não. fetch espera rede. Worker isola cálculo pesado noutro fio. São ferramentas diferentes. Aqui só o conceito do worker.

O arquivo completo

Referência do exemplo 22-javascript-assincrono, reproduzida aqui para que você possa conferir sem sair da página.

index.html

<!DOCTYPE html>

<html lang="pt-BR">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<title>Busca assíncrona</title>
<style>
/* Apoio de leitura, teclado e tela estreita. */
img{max-width:100%;height:auto} html {overflow-wrap:anywhere} main {min-width:0} input,textarea,select,canvas {max-width:100%;box-sizing:border-box} fieldset {min-width:0}
pre {max-width:100%;overflow:auto} :focus-visible {outline:3px solid #0f3d3e;outline-offset:3px}
.skip {position:absolute;top:-5rem;left:1rem;background:#0f3d3e;color:white;padding:.5rem;z-index:9}.skip:focus {top:.5rem}
table {max-width:100%} td,th {overflow-wrap:anywhere}
@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
</style></head>
<body><a class="skip" href="#conteudo">Pular para o conteúdo</a><main id="conteudo" tabindex="-1">
<h1>Buscar títulos</h1>
<p>Sirva esta pasta em 127.0.0.1.</p>
<button id="buscar" type="button">Buscar</button>
<ul id="lista"></ul>

</main><script src="busca.js"></script></body>
</html>

busca.js

document.getElementById("buscar").addEventListener("click", async function () {
  const lista = document.getElementById("lista");
  lista.textContent = "Buscando…";
  try {
    const resposta = await fetch("livros.json");
    if (!resposta.ok) throw new Error("HTTP " + resposta.status);
    const livros = await resposta.json();
    if (!Array.isArray(livros)) throw new Error("Formato inesperado");
    lista.innerHTML = "";
    if (!Array.isArray(livros)) throw new Error("Formato inesperado");
    if (!livros.length) lista.textContent = "Nenhum livro nesta lista.";
    livros.forEach(function (livro) {
      const item = document.createElement("li");
      item.textContent = livro.titulo;
      lista.appendChild(item);
    });
  } catch (erro) {
    lista.textContent = "Falhou. Use o servidor local.";
  }
});