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.
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:
primeiro;
terceiro;
segundo; // após 3 segundosVemos 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:
// 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.
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 é:
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.
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.

