Encadeando ações utilizando Promises. (Javascript)
Overview
Bem-vindos ao mundo das promises no Javascript! Se você já se perguntou como fazer o Javascript aguardar uma operação assíncrona antes de prosseguir, este post é para você. Com uma explicação divertida utilizando a analogia de tarefas domésticas e jogos, descomplicamos o conceito de promises. Vamos juntos explorar como criar, executar e encadear promises para melhorar seus códigos assíncronos. Prepare-se para tornar suas aplicações mais robustas e eficientes!
Você já tentou fazer uma requisição assíncrona no Javascript (com ou sem jQuery), aguardar o retorno desta execução e só depois fazer algo? Se a resposta for sim, então você sabe que o JS não vai esperar a resposta da requisição para executar os outros comandos. O que faz muito sentido, uma vez que estão sendo feitas requisições assíncronas.
Uma das formas de contornar isso e de encadear execução de tarefas no JS é utilizando as promises… e é bem simples!
O nome foi bem escolhido, pois já explica bem o que está acontecendo. As promises são promessas de que algo vai acontecer. Então se alguém promete que vai fazer algo e te responder, você vai esperar esta resposta.
Para ajudar no entendimento:
- Um dos pais vira para criança e fala: Se você fizer o dever de casa, pode ir jogar LoL hoje!
- A criança vai aceitar e isso se tornará uma promessa, ou seja, você está aguardando que ela faça o dever. Assim que ela terminar, você vai liberar o computador e deixa-la jogar LoL.
- Todavia, se ela enrolar e não fizer o dever, ela não poderá usar o computador para jogar LoL.
A analogia acima representa bem o que são as promises do JS.
Agora vamos começar a olhar para a sintaxe das promises…
let promiseDoHomework = new Promise(function(resolve, reject) {
//code to do homework...
let homeWorkDone = true;
if (homeWorkDone)
resolve();
else
reject();
});
O exemplo acima mostra a promise (promessa) de fazer o dever de casa.
- É criada uma instancia da classe Promise;
- Ela recebe uma função de callback que possui dois argumentos: resolve e reject;
- Dentro desta função de callback, o código do que deve ser executado (no nosso caso, seria o código referente a fazer o dever de casa);
- Se tudo der certo e o dever for feito com sucesso, então a função resolve() é utilizada;
- Se o resultado for um erro, então chamamos a função reject();
- Nas promises, você não usa return, você pode indicar sucesso com a utilização da função resolve ou pode indicar falha com a função reject;
Com o exemplo acima, expliquei o funcionamento básico das promises. O próximo passo é fazer o encadeamento delas…
promiseDoHomework.then(function() {
console.log("Homework done!");
}).catch(function(){
console.log("Homework not done!");
});
No código acima, executei a promessa “promiseDoHomework” e utilize a função then, que é executada quando a a promessa é resolvida com sucesso. Já a função catch é executada quando a promessa falha. Neste ponto, é interessante mostrar que é possível retornar informações da execução da promises para a função then e/ou catch. Para isso, vou adaptar os códigos acima…
let promiseDoHomework = new Promise(function(resolve, reject) {
//code to do homework...
let homeWorkDone = true;
if (homeWorkDone)
resolve('ALL DONE!');
else
reject('NO CAN DO!');
});
promiseDoHomework.then(function(resolveReturn) {
console.log("Homework done! Return: " + resolveReturn);
}).catch(function(rejectReturn){
console.log("Homework not done! Return: " + rejectReturn);
});
A diferença entre os dois códigos:
- Adicionei uma mensagem de retorno nas funções resolve e reject;
- Nas funções passadas no then e no catch, adicionei os argumentos: resolveReturn, que recebe tudo que passarmos como argumento da função resolve e rejectReturn, que recebe tudo que passarmos para a função reject.
Bom, o que falta agora é encadear os acontecimentos. Continuando na analogia que fizemos antes, vamos imaginar a seguinte sequencia: Primeiro o dever de casa deve ser feito, depois jogar algum jogo e depois ler um livro.
function doHomework() {
return new Promise(function(resolve, reject) {
//code to do homework...
let homeWorkDone = true;
if (homeWorkDone)
resolve('HOMEWORK DONE!!');
else
reject('NO CAN DO!');
});
}
function playGame() {
return new Promise(function(resolve, reject){
//Play LoL or anything else....
let donePlaying = true;
if (donePlaying)
resolve('DONE PLAYING!');
else
reject('SOMETHING WENT WRONG!');
});
}
function readBook() {
return new Promise(function(resolve, reject) {
//Read a book...
let doneReading = true;
if (doneReading)
resolve('DONE READING!');
else
reject('CANT READ!');
});
}
Criei três funções no código acima. Uma para a “lógica” de fazer o dever de casa, outra para jogar um jogo e a terceira para ler um livro. Todas retornam uma promessa que pode ou não retornar o valor correto.
Agora é só encadear a execução delas:
doHomework().then(function(msg) {
console.log("MSG from doHomework: " + msg);
return playGame();
}).then(function(msg) {
console.log("MSG from playGame: " + msg);
return readBook();
}).then(function(msg){
console.log("MSG from readBook: " + msg);
console.log("All done!");
});
O código acima está funcionando da seguinte forma:
- A função doHomework é executada primeiro. Ela vai retornar a promise com a indicação se tudo correu bem (resolve) ou se deu errado (reject);
- Se tudo der certo, será chamada a função then, que possui um callback. Neste ponto, será registrada uma mensagem no console e a função playGame será executada e (assim como a doHomework), vai retornar uma promise;
- Similar ao item 2, porém a função chamada será a readBook;
- No final de tudo, será executado um último “then”, que registra algumas mensagens no console.
E se algo der errado? Como tratar os retornos com erro?
Bom, então temos que encadear as chamadas para funções then e catch.
doHomework().catch(function(error_msg){
console.log("#### ERROR DOING HOMEWORK! ####");
console.log("ERROR MSG from doHomework: "+ error_msg);
}).then(function(msg) {
console.log("MSG from doHomework: " + msg);
return playGame();
}).catch(function(error_msg){
console.log("#### ERROR PLAY GAME! ####");
console.log("ERROR MSG from playGame: "+ error_msg);
}).then(function(msg) {
console.log("MSG from playGame: " + msg);
return readBook();
}).then(function(msg){
console.log("MSG from readBook: " + msg);
console.log("All done!");
}).catch(function(error_msg){
console.log("#### ERROR READING BOOK! ####");
console.log("ERROR MSG from readBook: "+ error_msg);
});
No código acima, inclui uma chamada para a função catch para cada uma das promises que foram retornadas. Este exemplo também serve para duas coisas:
- Mostrar a sintaxe de encadeamento de promises;
- Mostrar que, assim como na vida real, encadear muitas promessas pode se tornar complicado bem rápido;
Para quem quiser, todos estes fontes estão disponíveis no meu Github! 🙂
Espero ter ajudado!