Introdução às Promises em JavaScript
Uma promise é um objeto que representa o resultado, seja bem-sucedido ou falho, de uma operação assíncrona. Simplificadamente, uma operação assíncrona é uma ação que ocorre independentemente do código ao seu redor, sem bloquear a execução desse código.
Uma promise pode estar em um dos seguintes estados:
- pending(estado de espera): estado inicial, a promise foi criada, mas a execução ainda não foi concluída.
- fulfilled(concluído com sucesso): a ação que a promise representa foi concluída com sucesso.
- rejected(concluído com erro): ocorreu um erro durante a execução da ação que a promise representa.
Para criar uma promise, usa-se o construtor Promise:
new Promise(executor);O construtor recebe uma função que é executada imediatamente ao criar a promise. Essa função geralmente representa operações assíncronas que podem levar algum tempo. Por exemplo, vamos definir uma promise muito simples:
const myPromise = new Promise(function () {
  console.log("Executando operação assíncrona");
});Neste caso, a função apenas exibe uma mensagem no console. Assim, ao executar este código, veremos a mensagem "Executando operação assíncrona" no console.
Quando uma promise é criado e sua função ainda não começou a ser executada, a promise entra no estado "pending", ou seja, aguarda a execução.
Para simular a assincronicidade, vamos definir vários promises:
const myPromise3000 = new Promise(function () {
  console.log("[myPromise3000] Executando operação assíncrona");
  setTimeout(() => console.log("[myPromise3000] Conclusão da operação assíncrona"), 3000);
});
const myPromise1000 = new Promise(function () {
  console.log("[myPromise1000] Executando operação assíncrona");
  setTimeout(() => console.log("[myPromise1000] Conclusão da operação assíncrona"), 1000);
});
const myPromise2000 = new Promise(function () {
  console.log("[myPromise2000] Executando operação assíncrona");
  setTimeout(() => console.log("[myPromise2000] Conclusão da operação assíncrona"), 2000);
});Aqui são definidos três promises idênticos. Para que cada um deles não seja executado imediatamente, eles usam a função setTimeout com um delay de alguns segundos. A duração do delay varia para diferentes promises. Neste caso, obteremos a seguinte saída no console:
[myPromise3000] Executando operação assíncrona [myPromise1000] Executando operação assíncrona [myPromise2000] Executando operação assíncrona [myPromise1000] Conclusão da operação assíncrona [myPromise2000] Conclusão da operação assíncrona [myPromise3000] Conclusão da operação assíncrona
Aqui, vemos que a primeira a começar a execução foi a promise myPromise3000, mas ela também foi a última a terminar, pois tinha o maior tempo de delay, 3 segundos. No entanto, esse delay não impediu a execução das outras promises.
resolve e reject
Normalmente, a função passada ao construtor Promise aceita dois parâmetros: resolve e reject, ambos também funções, que podem aceitar um argumento de qualquer tipo.
- Função resolve: chamada em caso de sucesso, pode receber um valor que será obtido como resultado da execução bem-sucedida.
- Função reject: chamada em caso de erro, pode receber um valor que representará alguma informação sobre o erro.
Execução Bem-Sucedida do Promise
Portanto, o primeiro parâmetro da função no construtor do Promise, a função resolve, é executada quando a operação é concluída com sucesso. Geralmente, passa-se para essa função um valor que representa o resultado da operação bem-sucedida. Esse valor pode ser qualquer objeto. Por exemplo, vamos passar uma string para essa função:
const myPromise = new Promise(function (resolve) {
  console.log("Executando operação assíncrona");
  resolve("Olá mundo!");
});A função resolve() é chamada no final da operação, após todas as ações terem sido realizadas. Ao chamar essa função, a promise muda para o estado fulfilled (concluído com sucesso).
É importante notar que, teoricamente, podemos retornar um resultado diretamente da função, mas isso não teria sentido prático:
const myPromise = new Promise(function (resolve, reject) {
  console.log("Executando operação assíncrona");
  return "Olá mundo!";
});Esse valor retornado não pode ser passado para fora. E se realmente precisarmos retornar algum resultado, ele deve ser passado através da função resolve.
Informação sobre Erro
O segundo parâmetro da função no construtor do Promise, a função reject, é chamada quando ocorre um erro. Normalmente, passa-se para essa função alguma informação sobre o erro, que pode ser qualquer objeto. Por exemplo:
const myPromise = new Promise(function (resolve, reject) {
  console.log("Executando operação assíncrona");
  reject("Dados incorretos fornecidos");
});Ao chamar a função reject(), a promise muda para o estado rejected (concluído com erro).
Combinação de resolve e reject
Podemos definir uma lógica na qual, dependendo das condições, ambas as funções são executadas:
const x = 4;
const y = 0;
const myPromise = new Promise(function (resolve, reject) {
  if (y === 0) {
    reject("Dados incorretos");
  } else {
    const z = x / y;
    resolve(z);
  }
});Nesse caso, se o valor da constante y for 0, reportamos um erro chamando a função reject. Se for diferente de 0, realizamos uma operação de divisão e passamos o resultado para a função resolve.