Skip to content

Tarefas Assíncronas com Promises em JavaScript

Introdução

Esse comportamento costumava ser tratado através de callbacks, no entanto, com o tempo isso se tornou tedioso de manter e a indentação do código se tornou cada vez mais problemática quanto mais validações e processos derivados precisavam ser realizados. Isso deu lugar à introdução das Promises no JavaScript.

As Promises do JavaScript foram introduzidas com o ECMAScript 6 e trouxeram para a linguagem uma forma mais concisa de trabalhar com funções assíncronas.

É importante notar que as Promises não são suportadas pelo Internet Explorer. Para obter esse comportamento é necessário usar polyfills.

Funções assíncronas incluem ações como leituras de arquivos, requisições a servidores (ajax), funções que esperam um certo período de tempo (como setTimeout), processos que podem ser intensivos para a CPU, como o hashing de informações, entre outros.

Callbacks

Callbacks são funções enviadas como parâmetros para outra função. Espera-se que a última execute o código recebido após algum tempo.

No caso da função setTimeout que usaremos como exemplo, ela recebe uma função como parâmetro que é executada assim que o período definido em milissegundos termina.

javascript
let asincrono = function (cb) {
  setTimeout(function () {
    cb('segundo');
  }, 3000);
};

console.log('primeiro');
asincrono(function (result) {
  console.log(result);  
});
console.log('terceiro');

O resultado no console seria:

javascript
primeiro;
terceiro;  
segundo; // após 3 segundos

Vemos que a impressão de "segundo" não acontece até que um tempo tenha passado (neste caso, o especificado em setTimeout de 3000 ms).

Aqui usamos uma função chamada cb (ela pode ter qualquer nome), para ser chamada uma vez que o processo assíncrono tenha terminado, neste caso, um intervalo de tempo de 3 segundos.

Promise()

As Promises do JavaScript recebem uma função com dois parâmetros (resolve, reject) que são funções chamadas uma vez que o processo é executado com sucesso (resolve) ou falha (reject).

As Promises têm três estados:

  • Pending (esperando para ser resolvida ou rejeitada)
  • Fulfilled (resolvida)
  • Rejected (rejeitada)

Reescrevendo o exemplo anterior com promises ficaria assim:

javascript
// promises são instanciadas com 'new'
let asincrono = new Promise(function (resolve, reject) {
  setTimeout(function () {
    resolve('segundo');
  }, 3000);
});

console.log('primeiro');
asincrono.then(function (result) {
  console.log(result);
});
console.log('terceiro');

A saída do console seria a mesma do exemplo anterior.

Outro uso das promises é poder saber quando uma consulta assíncrona, por exemplo, a um servidor, está concluída.

javascript
let datosDeServidor = function (url) {
  return new Promise(function (resolve, reject) {
    let xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onload = function () {
      resolve(xhr.responseText); // acessado com 'then()'
    };
    xhr.onerror = function () {
      reject(xhr.statusText); // acessado com 'catch()'
    };
    xhr.send();
  });
};

datosDeServidor(/* url */)
  .then(function (response) {
    // consulta concluída
    console.log(response); // xhr.responseText
  })
  .catch(function (error) {
    // erro na requisição
    console.log(error); // xhr.statusText
  });

Vemos que then() é usado para resolve e catch() para reject.

Muitas das bibliotecas usadas em JavaScript e NodeJs suportam o uso de Promises. Por exemplo, axios, usado para fazer requisições ajax, é baseado em Promises, então seu uso geral é:

javascript
axios({
  /* options */
})
  .then(function (response) {
    // requisição bem-sucedida
    console.log(response);
  })
  .catch(function (error) {
    // falha na requisição
    console.log(error);
  });

Promise.all()

Promise.all() recebe um array de promises e retorna um array com as promises uma vez que elas tenham sido resolvidas. Usando como exemplo a consulta a um servidor, poderíamos usar Promise.all() se tivermos várias consultas assíncronas nas quais queremos agir assim que todas terminarem.

javascript
Promise.all([
  datosDeServidor(/* url do servidor x */),
  datosDeServidor(/* url do servidor y */),
  datosDeServidor(/* url do servidor z */),
])
  .then(function (results) {
    console.log(results[0]); // resposta do servidor x
    console.log(results[1]); // resposta do servidor y
    console.log(results[2]); // resposta do servidor z
  })
  .catch(function (error) {
    console.log(error); // erro da primeira promise que falhar
  });

As Promises do JavaScript nos dão maior flexibilidade ao lidar com código assíncrono. Um dos usos mais comuns das promises no desenvolvimento web é poder controlar o fluxo de requisições (ajax) a servidores web, já que, dependendo da conexão e outros fatores, o tempo exato que leva para uma resposta é imprevisível.