Encadeando ações utilizando Promises. (Javascript)

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.

  1. É criada uma instancia da classe Promise;
  2. Ela recebe uma função de callback que possui dois argumentos: resolve e reject;
  3. 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();
  4. 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:

  1. Adicionei uma mensagem de retorno nas funções resolve e reject;
  2. 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:

  1. 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);
  2. 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;
  3. Similar ao item 2, porém a função chamada será a readBook;
  4. 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:

  1. Mostrar a sintaxe de encadeamento de promises;
  2. 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!