November 9, 2023

Requisições, Fetch e Promises

A API fetch é uma interface JavaScript moderna para a realização de requisições HTTP, como solicitar recursos de um servidor. Ela é a evolução da antiga XMLHttpRequest e oferece uma abordagem mais poderosa e flexível, além de ter uma sintaxe mais limpa e fácil de entender. Vamos desmembrar os conceitos:

Promises

Antes de nos aprofundarmos em fetch, é importante entender o conceito de "Promises" (Promessas). Uma "Promise" é um objeto que representa a eventual conclusão (ou falha) de uma operação assíncrona e seu valor resultante. Em termos simples, é uma maneira de lidar com operações assíncronas de maneira mais gerenciável e menos propensa a erros do que as tradicionais funções de callback.

Uma "Promise" pode estar em um de três estados:

  1. Pending (Pendente): Estado inicial, não preenchido nem rejeitado.
  2. Fulfilled (Realizada): Significa que a operação foi concluída com sucesso.
  3. Rejected (Rejeitada): Significa que a operação falhou.

E como funcionam as Promises?

Promises têm um método chamado .then(), que recebe uma função callback e retorna um "objeto-promessa". Não é um retorno dos dados, é a promessa do retorno destes dados.

Assim, podemos escrever o código do que irá acontecer em seguida, com os dados recebidos pela Promise, e o JavaScript vai aguardar a resolução da Promise sem pausar o fluxo do programa.

O resultado pode ou não estar pronto ainda, e não há forma de pegar o valor de uma Promise de modo síncrono; Só é possível requisitar à Promise que execute uma função quando o resultado estiver disponível - seja ele o que foi solicitado (os dados da API, por exemplo), ou uma mensagem de erro caso algo tenha dado errado com a requisição (o servidor pode estar fora do ar, por exemplo).

No exemplo acima: ao iniciarmos uma cadeia de promessas - no caso, para fazer uma requisição HTTP - enquanto a resposta está pendente ela retorna um Promise object. O objeto, por sua vez, define uma instância do método .then(). Ao invés de passar o retorno da função callback diretamente para a função inicial, ela é passada para .then(). Quando o resultado da requisição HTTP chega, o corpo da requisição é convertido para JSON e este valor convertido é passado para o próximo método .then().

A cadeia de funções fetch().then().then() não significa que há múltiplas funções callbacks sendo usadas com o mesmo objeto de resposta, e sim que cada instância de .then() retorna, por sua vez, um new Promise(). Toda a cadeia é lida de forma síncrona na primeira execução, e em seguida executada de forma assíncrona.

Fetch

Agora, vamos ao fetch. A função fetch() inicia o processo de "buscar" um recurso da rede. Ela retorna uma "Promise" que resolve com o objeto Response representando a resposta a sua solicitação, seja ela bem-sucedida ou não. Como fetch é baseado em "Promises", ele utiliza os métodos .then() e .catch() para lidar com os valores resolvidos ou rejeitados.

Aqui está a estrutura básica de como fetch é utilizado:

fetch('url-aqui')                // Passo 1: Iniciar uma nova requisição para a URL especificada.
  .then(response => {            // Passo 2: Quando a "Promise" é resolvida, receber um objeto "Response".
    if (!response.ok) {          // Verificar se a resposta foi bem-sucedida.
      throw new Error('Erro na rede ao tentar buscar recurso.');
    }
    return response.json();      // Se bem-sucedida, ler e parsear o JSON ou qualquer outro tipo de dado.
  })
  .then(data => {                // Passo 3: Trabalhar com os dados recebidos (agora no formato JavaScript).
    console.log(data);
  })
  .catch(error => {              // Passo 4: Capturar qualquer erro que tenha ocorrido durante a requisição.
    console.error('Houve um problema com a operação fetch:', error);
  });

Explicando o processo:

  1. Iniciando uma Fetch Request: fetch('url') retorna uma "Promise". O JavaScript continuará executando outro código enquanto espera que a promessa seja resolvida.
  2. Manipulando a resposta: O método .then() é usado para especificar o que fazer com a resposta. Ele aceita um callback com a resposta. No entanto, esta resposta é um objeto Response do stream, e para consumir o corpo da resposta em um tipo específico (como JSON), você precisa usar métodos específicos do objeto Response, como .json(). Este método também retorna uma "Promise", porque ler o stream pode ser uma operação que leva tempo.
  3. Usando o resultado: Uma vez que os dados foram parseados (por exemplo, via .json()), você pode usar outro .then() para trabalhar com os dados no formato JavaScript.
  4. Capturando erros: Se algo der errado na solicitação (como problemas de rede, resposta inválida, etc.), .catch() é executado, permitindo que você capture o erro e decida o que fazer com ele (como informar ao usuário).

Benefícios e Características

  • Sintaxe Limpa: fetch oferece uma maneira mais limpa de lidar com requisições AJAX, especialmente quando comparado com a antiga XMLHttpRequest.
  • Manuseio de Erro: Com fetch, você tem um controle mais granular sobre o tratamento de erros. Por exemplo, até mesmo respostas HTTP 404 ou 500 não são consideradas erros de rede e não resultarão em um catch. Em vez disso, ele trata qualquer resposta como um sucesso, desde que a requisição tenha recebido uma resposta.
  • Flexibilidade: fetch é muito mais flexível e modular do que as abordagens antigas, permitindo que você escolha quais requisições e respostas manipular, em vez de ter que receber o pacote completo.

No entanto, é importante notar que, embora fetch seja fantástico, ele não é suportado em navegadores mais antigos. Em projetos que requerem compatibilidade com navegadores mais antigos, bibliotecas como Axios ou o pollyfill para fetch podem ser utilizadas.

db.json

{
  "socios": [
    {
      "id": 1,
      "nome": "João Silva",
      "plano": "Bronze",
      "telefone": "1122334455",
      "email": "[email protected]",
      "status": "ativo"
    },
    {
      "id": 2,
      "nome": "Mariana Pereira",
      "plano": "Ouro",
      "telefone": "1122334466",
      "email": "[email protected]",
      "status": "ativo"
    },
    {
      "id": 3,
      "nome": "Lucas Moura",
      "plano": "Prata",
      "telefone": "1122334477",
      "email": "[email protected]",
      "status": "desativo"
    },
    {
      "id": 4,
      "nome": "Patricia Santos",
      "plano": "Bronze",
      "telefone": "1122334488",
      "email": "[email protected]",
      "status": "ativo"
    },
    {
      "id": 5,
      "nome": "Roberto Almeida",
      "plano": "Ouro",
      "telefone": "1122334499",
      "email": "[email protected]",
      "status": "desativo"
    },
    {
      "id": 6,
      "nome": "Camila Dias",
      "plano": "Prata",
      "telefone": "1122334500",
      "email": "[email protected]",
      "status": "ativo"
    },
    {
      "id": 7,
      "nome": "Carlos Oliveira",
      "plano": "Bronze",
      "telefone": "1122334511",
      "email": "[email protected]",
      "status": "ativo"
    }
  ]
}