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…
1let promiseDoHomework = new Promise(function(resolve, reject) {
2 //code to do homework...
3 let homeWorkDone = true;
4 if (homeWorkDone)
5 resolve();
6 else
7 reject();
8});
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…
1promiseDoHomework.then(function() {
2 console.log("Homework done!");
3}).catch(function(){
4 console.log("Homework not done!");
5});
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…
1let promiseDoHomework = new Promise(function(resolve, reject) {
2 //code to do homework...
3 let homeWorkDone = true;
4 if (homeWorkDone)
5 resolve('ALL DONE!');
6 else
7 reject('NO CAN DO!');
8});
9
10
11promiseDoHomework.then(function(resolveReturn) {
12 console.log("Homework done! Return: " + resolveReturn);
13 }).catch(function(rejectReturn){
14 console.log("Homework not done! Return: " + rejectReturn);
15 });
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.
1function doHomework() {
2 return new Promise(function(resolve, reject) {
3 //code to do homework...
4 let homeWorkDone = true;
5 if (homeWorkDone)
6 resolve('HOMEWORK DONE!!');
7 else
8 reject('NO CAN DO!');
9 });
10}
11
12function playGame() {
13 return new Promise(function(resolve, reject){
14 //Play LoL or anything else....
15 let donePlaying = true;
16 if (donePlaying)
17 resolve('DONE PLAYING!');
18 else
19 reject('SOMETHING WENT WRONG!');
20 });
21}
22
23function readBook() {
24 return new Promise(function(resolve, reject) {
25 //Read a book...
26 let doneReading = true;
27 if (doneReading)
28 resolve('DONE READING!');
29 else
30 reject('CANT READ!');
31 });
32}
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:
1doHomework().then(function(msg) {
2 console.log("MSG from doHomework: " + msg);
3 return playGame();
4}).then(function(msg) {
5 console.log("MSG from playGame: " + msg);
6 return readBook();
7}).then(function(msg){
8 console.log("MSG from readBook: " + msg);
9 console.log("All done!");
10});
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.
1doHomework().catch(function(error_msg){
2 console.log("#### ERROR DOING HOMEWORK! ####");
3 console.log("ERROR MSG from doHomework: "+ error_msg);
4
5}).then(function(msg) {
6 console.log("MSG from doHomework: " + msg);
7 return playGame();
8
9}).catch(function(error_msg){
10 console.log("#### ERROR PLAY GAME! ####");
11 console.log("ERROR MSG from playGame: "+ error_msg);
12
13}).then(function(msg) {
14 console.log("MSG from playGame: " + msg);
15 return readBook();
16
17}).then(function(msg){
18 console.log("MSG from readBook: " + msg);
19 console.log("All done!");
20
21}).catch(function(error_msg){
22 console.log("#### ERROR READING BOOK! ####");
23 console.log("ERROR MSG from readBook: "+ error_msg);
24
25});
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!