Promessas de JavaScript

Resumo : neste tutorial, você aprenderá sobre as promessas do JavaScript e como usá-las de maneira eficaz.

Por que o JavaScript promete

O exemplo a seguir define uma função getUsers() que retorna uma lista de objetos de usuário :

function getUsers() {
  return [
    { username: 'john', email: '[email protected]' },
    { username: 'jane', email: '[email protected]' },
  ];
}Linguagem de código:  JavaScript  ( javascript )

Cada objeto de usuário possui duas propriedades usernamee email.

Para encontrar um usuário pelo nome de usuário na lista de usuários retornada pela getUsers()função, você pode usar a findUser()função da seguinte maneira:

function findUser(username) {
  const users = getUsers();
  const user = users.find((user) => user.username === username);
  return user;
}Linguagem de código:  JavaScript  ( javascript )

Na findUser()função:

  • Primeiro, obtenha um array de usuário chamando a getUsers()função
  • Segundo, encontre o usuário específico usernameusando o find()método do Arrayobjeto.
  • Terceiro, retorne o usuário correspondente.

O seguinte mostra o código completo para encontrar um usuário com o nome de usuário 'john':

function getUsers() {
  return [
    { username: 'john', email: '[email protected]' },
    { username: 'jane', email: '[email protected]' },
  ];
}

function findUser(username) {
  const users = getUsers(); 
  const user = users.find((user) => user.username === username);
  return user;
}

console.log(findUser('john'));
Linguagem de código:  JavaScript  ( javascript )

Saída:

{ username: 'john', email: '[email protected]' }Linguagem de código:  CSS  ( css )

O código na findUser()função é síncrono e bloqueador. A findUser()função executa a getUsers()função para obter um array de usuário, chama o find()método no usersarray para procurar um usuário com um nome de usuário específico e retorna o usuário correspondente.

Na prática, a getUsers()função pode acessar um banco de dados ou chamar uma API para obter a lista de usuários. Portanto, a getUsers()função terá um atraso.

Para simular o atraso, você pode usar a setTimeout()função. Por exemplo:

function getUsers() {
  let users = [];

  // delay 1 second (1000ms)
  setTimeout(() => {
    users = [
      { username: 'john', email: '[email protected]' },
      { username: 'jane', email: '[email protected]' },
    ];
  }, 1000);

  return users;
}Linguagem de código:  JavaScript  ( javascript )

Como funciona.

  • Primeiro, defina um array userse inicialize seu valor com um array vazio.
  • Segundo, atribua um array de usuários à usersvariável dentro do retorno de chamada da setTimeout()função.
  • Terceiro, retorne o usersarray

O getUsers()não funcionará corretamente e sempre retornará um array vazio. Portanto, a findUser()função não funcionará conforme o esperado:

function getUsers() {
  let users = [];
  setTimeout(() => {
    users = [
      { username: 'john', email: '[email protected]' },
      { username: 'jane', email: '[email protected]' },
    ];
  }, 1000);
  return users;
}

function findUser(username) {
  const users = getUsers(); // A
  const user = users.find((user) => user.username === username); // B
  return user;
}

console.log(findUser('john'));
Linguagem de código:  JavaScript  ( javascript )

Saída:

undefinedLinguagem de código:  JavaScript  ( javascript )

Como getUsers()retorna um array vazio, o usersarray está vazio (linha A). Ao chamar o find()método no usersarray, o método retorna undefined(linha B)

O desafio é como acessar o usersretornado da getUsers()função após um segundo. Uma abordagem clássica é usar o callback .

Usando retornos de chamada para lidar com uma operação assíncrona

O exemplo a seguir adiciona um argumento de retorno de chamada às funções getUsers()e :findUser()

function getUsers(callback) {
  setTimeout(() => {
    callback([
      { username: 'john', email: '[email protected]' },
      { username: 'jane', email: '[email protected]' },
    ]);
  }, 1000);
}

function findUser(username, callback) {
  getUsers((users) => {
    const user = users.find((user) => user.username === username);
    callback(user);
  });
}

findUser('john', console.log);Linguagem de código:  JavaScript  ( javascript )

Saída:

{ username: 'john', email: '[email protected]' }Linguagem de código:  CSS  ( css )

Neste exemplo, a getUsers()função aceita uma função de retorno de chamada como argumento e a invoca com o usersarray dentro da setTimeout()função. Além disso, a findUser()função aceita uma função de retorno de chamada que processa o usuário correspondente.

A abordagem de retorno de chamada funciona muito bem. No entanto, isso torna o código mais difícil de seguir. Além disso, adiciona complexidade às funções com argumentos de retorno de chamada.

Se o número de funções aumentar, você poderá acabar com o problema do inferno de retorno de chamada. Para resolver isso, o JavaScript surge com o conceito de promessas.

Compreendendo as promessas do JavaScript

Por definição, uma promessa é um objeto que encapsula o resultado de uma operação assíncrona .

Um objeto de promessa possui um estado que pode ser um dos seguintes:

  • Pendente
  • Cumprido com um valor
  • Rejeitado por um motivo

No início, o estado de uma promessa está pendente, indicando que a operação assíncrona está em andamento. Dependendo do resultado da operação assíncrona, o estado muda para cumprido ou rejeitado.

O estado cumprido indica que a operação assíncrona foi concluída com êxito:

Promessa JavaScript cumprida

O estado rejeitado indica que a operação assíncrona falhou.

Criando uma promessa

Para criar um objeto de promessa, você usa o Promise()construtor:

const promise = new Promise((resolve, reject) => {
  // contain an operation
  // ...

  // return the state
  if (success) {
    resolve(value);
  } else {
    reject(error);
  }
});Linguagem de código:  JavaScript  ( javascript )

O construtor da promessa aceita uma função de retorno de chamada que normalmente executa uma operação assíncrona. Essa função costuma ser chamada de executor.

Por sua vez, o executor aceita duas funções de retorno de chamada com o nome resolvee reject.

Observe que as funções de retorno de chamada passadas para o executor são resolvee rejectapenas por convenção.

Se a operação assíncrona for concluída com êxito, o executor chamará a resolve()função para alterar o estado da promessa de pendente para cumprida com um valor.

Em caso de erro, o executor chamará a reject()função para alterar o estado da promessa de pendente para rejeitada com o motivo do erro.

Quando uma promessa atinge um estado cumprido ou rejeitado, ela permanece nesse estado e não pode ir para outro estado.

Em outras palavras, uma promessa não pode ir de fulfilledestado para rejectedestado e vice-versa. Além disso, não pode voltar de um fulfilledestado rejectedpara outro pending.

Depois que um novo Promiseobjeto é criado, seu estado fica pendente. Se uma promessa for alcançada fulfilledou rejecteddeclarada, ela será resolvida .

Observe que você raramente criará objetos de promessa na prática. Em vez disso, você consumirá promessas fornecidas pelas bibliotecas.

Consumindo uma promessa: então, pegue, finalmente

1) O método then()

Para obter o valor de uma promessa quando ela for cumprida, você chama o then()método do objeto da promessa. O seguinte mostra a sintaxe do then()método:

promise.then(onFulfilled,onRejected);Linguagem de código:  CSS  ( css )

O then()método aceita duas funções de retorno de chamada: onFulfillede onRejected.

O then()método chama the onFulfilled()com um valor, se a promessa for cumprida ou onRejected()com um erro se a promessa for rejeitada.

Observe que ambos os argumentos onFulfillede onRejectedsão opcionais.

O exemplo a seguir mostra como usar then()o método do Promiseobjeto retornado pela getUsers()função:

function getUsers() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve([
        { username: 'john', email: '[email protected]' },
        { username: 'jane', email: '[email protected]' },
      ]);
    }, 1000);
  });
}

function onFulfilled(users) {
  console.log(users);
}

const promise = getUsers();
promise.then(onFulfilled);Linguagem de código:  JavaScript  ( javascript )

Saída:

[
  { username: 'john', email: '[email protected]' },
  { username: 'jane', email: '[email protected]' }
]Linguagem de código:  JavaScript  ( javascript )

Neste exemplo:

  • Primeiro, defina a onFulfilled()função a ser chamada quando a promessa for cumprida.
  • Segundo, chame a getUsers()função para obter um objeto de promessa.
  • Terceiro, chame o then()método do objeto de promessa e envie a lista de usuários para o console.

Para tornar o código mais conciso, você pode usar uma função de seta como argumento do then()método como este:

function getUsers() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve([
        { username: 'john', email: '[email protected]' },
        { username: 'jane', email: '[email protected]' },
      ]);
    }, 1000);
  });
}

const promise = getUsers();

promise.then((users) => {
  console.log(users);
});
Linguagem de código:  JavaScript  ( javascript )

Como a getUsers()função retorna um objeto de promessa, você pode encadear a chamada da função com o then()método assim:

// getUsers() function
//...

getUsers().then((users) => {
  console.log(users);
});
Linguagem de código:  JavaScript  ( javascript )

Neste exemplo, a getUsers()função sempre é bem-sucedida. Para simular o erro, podemos usar um successsinalizador como o seguinte:

let success = true;

function getUsers() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (success) {
        resolve([
          { username: 'john', email: '[email protected]' },
          { username: 'jane', email: '[email protected]' },
        ]);
      } else {
        reject('Failed to the user list');
      }
    }, 1000);
  });
}

function onFulfilled(users) {
  console.log(users);
}
function onRejected(error) {
  console.log(error);
}

const promise = getUsers();
promise.then(onFulfilled, onRejected);Linguagem de código:  JavaScript  ( javascript )

Como funciona.

Primeiro, defina a successvariável e inicialize seu valor como true.

Se o sucesso for true, a promessa na getUsers()função é cumprida com uma lista de usuários. Caso contrário, será rejeitado com uma mensagem de erro.

Em segundo lugar, defina as funções onFulfillede onRejected.

Terceiro, obtenha a promessa da getUsers()função e chame o then()método com as funções onFulfillede onRejected.

O seguinte mostra como usar as funções de seta como argumentos do then()método:

// getUsers() function
// ...

const promise = getUsers();
promise.then(
  (users) => console.log,
  (error) => console.log
);Linguagem de código:  JavaScript  ( javascript )

2) O método catch()

Se você deseja obter o erro somente quando o estado da promessa for rejeitado, você pode usar o catch()método do Promiseobjeto:

promise.catch(onRejected);Linguagem de código:  CSS  ( css )

Internamente, o catch()método invoca o then(undefined, onRejected)método.

O exemplo a seguir altera o successsinalizador para falsepara simular o cenário de erro:

let success = false;

function getUsers() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (success) {
        resolve([
          { username: 'john', email: '[email protected]' },
          { username: 'jane', email: '[email protected]' },
        ]);
      } else {
        reject('Failed to the user list');
      }
    }, 1000);
  });
}

const promise = getUsers();

promise.catch((error) => {
  console.log(error);
});Linguagem de código:  JavaScript  ( javascript )

3) O método finalmente()

Às vezes, você deseja executar o mesmo trecho de código, independentemente de a promessa ser cumprida ou rejeitada. Por exemplo:


const render = () => {
  //...
};

getUsers()
  .then((users) => {
    console.log(users);
    render();
  })
  .catch((error) => {
    console.log(error);
    render();
  });Linguagem de código:  JavaScript  ( javascript )

Como você pode ver, a render()chamada de função é duplicada nos métodos then()e catch().

Para remover esta duplicata e executar render()se a promessa foi cumprida ou rejeitada, você usa o finally()método assim:


const render = () => {
  //...
};

getUsers()
  .then((users) => {
    console.log(users);
  })
  .catch((error) => {
    console.log(error);
  })
  .finally(() => {
    render();
  });
Linguagem de código:  JavaScript  ( javascript )

Um exemplo prático de promessa de JavaScript

O exemplo a seguir mostra como carregar um arquivo JSON do servidor e exibir seu conteúdo em uma página da web.

Suponha que você tenha o seguinte arquivo JSON:

https://tutorials.acervolima.com/sample/promise/api.jsonLinguagem de código:  JavaScript  ( javascript )

com o seguinte conteúdo:

{
    "message": "JavaScript Promise Demo"
}Linguagem de código:  JSON/JSON com comentários  ( json )

O seguinte mostra a página HTML que contém um botão. Ao clicar no botão, a página carrega os dados do arquivo JSON e mostra a mensagem:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>JavaScript Promise Demo</title>
    <link href="css/style.css" rel="stylesheet">
</head>
<body>
    <div id="container">
        <div id="message"></div>
        <button id="btnGet">Get Message</button>
    </div>
    <script src="js/promise-demo.js">
    </script>
</body>
</html>Linguagem de código:  HTML, XML  ( xml )

O seguinte mostra o arquivo promessa-demo.js:

function load(url) {
  return new Promise(function (resolve, reject) {
    const request = new XMLHttpRequest();
    request.onreadystatechange = function () {
      if (this.readyState === 4 && this.status == 200) {
        resolve(this.response);
      } else {
        reject(this.status);
      }
    };
    request.open('GET', url, true);
    request.send();
  });
}

const url = 'https://tutorials.acervolima.com/sample/promise/api.json';
const btn = document.querySelector('#btnGet');
const msg = document.querySelector('#message');

btn.addEventListener('click', () => {
  load(URL)
    .then((response) => {
      const result = JSON.parse(response);
      msg.innerHTML = result.message;
    })
    .catch((error) => {
      msg.innerHTML = `Error getting the message, HTTP status: ${error}`;
    });
});
Linguagem de código:  JavaScript  ( javascript )

Como funciona.

Primeiro, defina a load()função que utiliza o XMLHttpRequestobjeto para carregar o arquivo JSON do servidor:

function load(url) {
  return new Promise(function (resolve, reject) {
    const request = new XMLHttpRequest();
    request.onreadystatechange = function () {
      if (this.readyState === 4 && this.status == 200) {
        resolve(this.response);
      } else {
        reject(this.status);
      }
    };
    request.open('GET', url, true);
    request.send();
  });
}Linguagem de código:  JavaScript  ( javascript )

No executor, chamamos resolve()a função com a Resposta se o código de status HTTP for 200. Caso contrário, invocamos a reject()função com o código de status HTTP.

Em segundo lugar, registre o ouvinte de evento de clique do botão e chame o then()método do objeto de promessa. Se o carregamento for bem-sucedido, mostramos a mensagem retornada do servidor. Caso contrário, mostramos a mensagem de erro com o código de status HTTP.


const url = 'https://tutorials.acervolima.com/sample/promise/api.json';
const btn = document.querySelector('#btnGet');
const msg = document.querySelector('#message');

btn.addEventListener('click', () => {
  load(URL)
    .then((response) => {
      const result = JSON.parse(response);
      msg.innerHTML = result.message;
    })
    .catch((error) => {
      msg.innerHTML = `Error getting the message, HTTP status: ${error}`;
    });
});
Linguagem de código:  JavaScript  ( javascript )

Resumo

  • Uma promessa é um objeto que encapsula o resultado de uma operação assíncrona.
  • Uma promessa começa no estado pendente e termina no estado cumprido ou rejeitado.
  • Use then()o método para agendar um retorno de chamada a ser executado quando a promessa for cumprida e catch()o método para agendar um retorno de chamada a ser invocado quando a promessa for rejeitada.
  • Coloque o código que você deseja executar no finally()método, independentemente de a promessa ser cumprida ou rejeitada.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *