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…

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.

  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…

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:

  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.

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:

  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.

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:

  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!