Promesas en JavaScript. Resolviendo el infierno de los callbacks

Las Promesas de JavaScript vinieron a resolver una problema al que se enfrentaron miles de desarrolladores. Muchos nos enfrentamos al infierno de los callbacks pero eso era lo que había. Ahora tenemos que mirar las promesas como la panacea de los callbacks.

Cuando trabajamos con funciones asíncronas sabemos que vamos a recibir una respuesta pero no sabemos en que momento o cuanto tiempo va a demorar. Así que alguien muy listo se saco de la manga los callbacks que en JavaScript son como su propio nombre indica, “llamadas hacia atras”. Esto quiere decir que cuando llamo una función pesándole como parámetro otra función (el callback) la función (parámetro) se ejecuta cuando la función principal ha terminado de ejecutarse. Vea el siguiente ejemplo y su problemática:

<!DOCTYPE html>
   <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <title></title>
      <meta name="description" content="">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="stylesheet" href="">
   </head>
   <body>
      <h1>Callbacks</h1>
      <script>
         function SumarUno( numero ) {
            // simulamos una respuesta asíncrona.
            setTimeout( function() {
               return numero + 1;
            }, 1000);
         }
         console.log( SumarUno( 5 ) );
      </script>
   </body>
</html>

Si ejecutamos está página web y revisamos la consola obtendremos un espantoso undefined ¿Por que? Porqué la función SumarUno() se ejecuta primero y esta devuelve un undefined, devuelve “nada” y 1000 milisegundos después se ejecuta el setTimeout() que devuelve el número que le enviamos como parámetro + 1, pero ya es demasiado tarde por que la función principal ya terminó. Veamos como podemos resolver este problema con callbacks.

   <script>
         function SumarUno( numero, callback ) {
            // simulamos una respuesta asíncrona.
            setTimeout( function() {
               callback( numero + 1 );
            }, 1000);
         }
         SumarUno(5, function( nuevoNum ){
            console.log( nuevoNum );
         });

Si volvemos a ejecutar esta página en el navegador veremos que ahora si devuelve un 6 después de un segundo, ¿porque? porqué lo que estamos haciendo es que primero se ejecuta la función principal SumarUno() y un segundo después se ejecuta el callback, o la función, que enviamos como parámetro. La función principal se ve obligada a espere el tiempo que sea necesario para que se ejecute el callback.

Hasta aquí todo es el paraíso pero cuando manejamos varios callbacks esto se vuelve un infierno. Velo por ti mismo:

<script>
   function SumarUno( numero, callback ) {
      // Simulamos un error.
      if (numero >= 7) {
         callback('Valor muy alto: ' + numero);
         return;
      }
      // simulamos una respuesta asíncrona.
      setTimeout( function() {
         callback(null, numero + 1 );
      }, 3000);
   }
   SumarUno(5, function(error,  nuevoNum ){
      if( error ) {
         console.log( error );
         return;
      }
      SumarUno( nuevoNum, function(error, nuevoNum2) {
         if( error ) {
            console.log( error );
            return;
         }
         SumarUno( nuevoNum2, function (error,  nuevoNum3 ) {
            if( error ) {
               console.log( error );
               return;
            }
            console.log(nuevoNum3);
         });
      });
   });
</script>

En el código de arriba vemos como después de 3 callbacks se va complicando. Y si agregamos un código para validar errores esto ya se vuelve todavía mas complicado. ¿Que pasaría si estamos manejando 5 o 6 callbacks?, pues esto ya se convertiría en un infierno.

Ahora convertiremos todo ese infierno de arriba en un paraíso usando las promesas de JavaScript:

<script>
   function SumarUno(numero) {
      let promesa = new Promise(function (resolv, reject) {
         // simulamos una respuesta asíncrona.
         setTimeout(function () {
            resolv(numero + 1);
         }, 1000);
      });
      return promesa;
   }
   SumarUno(5).then(nuevoNum => {
      console.log(nuevoNum);
      return SumarUno(nuevoNum);
   }).then(nuevoNum => {
      console.log(nuevoNum);
      return SumarUno(nuevoNum);
   }).then(nuevoNum => {
      console.log(nuevoNum);
   });
</script>

Observe bien como se modificó un poco la función principal SumarUno() agregando una promesa new Promise(). Las promesas reciben internamente dos callbacks o funciones una llamada resolve, que se ejecuta si todo sale bien; y la otra una función llamada reject por si se genera un error. El nombre de las funciones resolve reject se usan por convención, pero se pueden usar otros nombre.

Ahora al ejecutar la función SumarUno() solo le enviamos un parametro SumarUno(5) y para obtener el valor de la resolución de la promesa, (el resolve) es con la funcion then() quedaría de esta manera, SumarUno(5).then(). A su vez la función then() recibe otro callback o una función común y corriente: SumarUno( 5 ).then( function( nuevoNum ) { console.log(nuevoNum); }) también podemos usar una función de flecha. SumarUno(5).then(nuevoNum => { console.log(nuevoNum); }) que es más recomendable y legible.

Todavía podemos simplificar mas nuestros callbacks. Chequen esto:

<script>
   function SumarUno(numero) {
      let promesa = new Promise(function (resolv, reject) {
         // simulamos una respuesta asíncrona.
         setTimeout(function () {
            resolv(numero + 1);
         }, 1000);
      });
      return promesa;
   }
   SumarUno(5)
      .then(nuevoNum => SumarUno(nuevoNum) )
      .then(nuevoNum => SumarUno(nuevoNum) )
      .then(nuevoNum => console.log(nuevoNum) );
</script>

si creías que ya no se podía simplificar más, estas equivocado:

<script>
   function SumarUno(numero) {
      let promesa = new Promise(function (resolv, reject) {
         // simulamos una respuesta asíncrona.
         setTimeout(function () {
            resolv(numero + 1);
         }, 1000);
      });
      return promesa;
   }
   SumarUno(5)
      .then( SumarUno )
      .then( SumarUno )
      .then( nuevoNum => console.log(nuevoNum) );
</script>

¿Que hicimos arriba? trataré de explicar algo que se me hace complicado. En la función then( nuevoNum => SumarUno(nuevoNum) ) estabamos enviando el parámetro o valor a la función de flecha, pero si la respuesta es el argumento de lo que ustedes quieren mandar en la misma función no es necesario definir la función de flecha. O el retorno va a ser el valor que tenga la función SumarUno() y el argumento que esta resolviendo el then() es el argumento o son los argumentos que se van a mandar a la función SumarUno(). Mmm espero haberme explicado. Jajajaja.

Validando los errores en las promesas

Ahora veamos como podemos manejar los errores en las promesas. Que es lo mas sencillo que pueda haber. Pegaré nuevamente el código:

<script>
   function SumarUno(numero) {
      let promesa = new Promise(function (resolv, reject) {
         console.log(numero);
         // Simulamos error...
         if ( numero >= 8 ) {
            reject('El número enviado es muy alto.');
         }
         // simulamos una respuesta asíncrona.
         setTimeout(function () {
            resolv(numero + 1);
         }, 1000);
      });
      return promesa;
   }
   SumarUno(5)
      .then( SumarUno )
      .then( SumarUno )
      .then( SumarUno )
      .then( SumarUno )
      .then( SumarUno )
      .then( nuevoNum => console.log(nuevoNum) )
      .catch( error => {
         console.log("Se produjo un error");
         console.log(error);
      });
</script>

Hasta aquí dejamos las promesas y los callbacks hell. Seguiré escribiendo otros artículos detallando el uso de las promesas en JavaScript.

Publicado por jazdian

Desarrollador web freelancer

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s

A %d blogueros les gusta esto: