6.1 Justificación

Una “promesa” o también llamada “futuro” es un objeto que actúa como proxy en los casos en los que no se puede retornar el verdadero valor porque aún no se conoce pero no se puede bloquear la función esperando a que llegue. Es una forma de hacer las cosas en vez de usar las funciones de callback.

Voy a volver a explicarlo pero con un ejemplo: Al hacer una llamada Ajax con $http, la llamada a $http no retorna ningún valor ya que aún no tiene dicho valor pero tampoco se puede bloquear esperando a que llegue. Pues en realidad el servicio $http sí que retorna un valor. Ésto que retorna es una promesa y, como hemos dicho, la promesa es un proxy que en un futuro contendrá el valor.

Ahora que quizás ya entiendes la buena idea de las promesas, pasemos a la parte negativa. La promesa tendrá en un futuro el valor pero, ¿cómo nos enteramos de que ahora la promesa ya tiene el valor? Pues usando una función de callback. ¿Cómo?!!, ¿No habíamos dicho que lo bueno de las promesas es evitar las funciones de callback?

Pirámide de la muerte

Realmente no podemos evitar las funciones de callback pero sí que podemos evitar lo que se llama la pirámide de la muerte (en ingles Pyramid of Doom)). La pirámide de la muerte se produce cuando dentro de una función de callback hacemos otra llamada asíncrona que a su vez tiene otra función de callback y dentro de esta nueva función de callback hacemos otra llamada asíncrona, y así sucesivamente.

Podemos ver cómo queda la pirámide de la muerte en el siguiente ejemplo.

Obtenemos el fichero json “fichero1.json” y leemos el valor de “importe” pero dentro volvemos a obtener el fichero “fichero2.json” y obtenemos el valor del importe que sumamos al primer importe, …..y así hasta 4 veces.

    $scope.importeTotal = 0;
    $scope.mensajeFinal = "";
    
    $http({method: 'GET',url: 'fichero1.json'}).success(function(data, status, headers, config) {
      $scope.importeTotal = $scope.importeTotal + data.importe;
      $http({method: 'GET',url: 'fichero2.json'}).success(function(data, status, headers, config) {
        $scope.importeTotal = $scope.importeTotal + data.importe;
        $http({method: 'GET',url: 'fichero3.json'}).success(function(data, status, headers, config) {
          $scope.importeTotal = $scope.importeTotal + data.importe;
          $http({method: 'GET',url: 'fichero4.json'}).success(function(data, status, headers, config) {
            $scope.importeTotal = $scope.importeTotal + data.importe;
            $scope.mensajeFinal = "Ya hemos finalizado la lista de cálculos";
          });
        });
      });
    });

Hemos quitado las funciones de error para que se vea mejor el ejemplo.

Vemos cómo estamos añadiendo funciones dentro de funciones lo que hace que el código esté cada vez mas identado lo que crea una forma de pirámide y da nombre al problema.

El problema de ésto es que el código es poco modular, cada una de las funciones de callback está dentro de la anterior, haciendo que separarlas sea complejo.

Veamos ahora el mismo ejemplo usando promesas

    $scope.importeTotalPromesas = 0;
    $scope.mensajeFinalPromesas="";
    
    $http({ method: 'GET',url: 'fichero1.json'}).then(function(resultado) {
      $scope.importeTotalPromesas = $scope.importeTotalPromesas + resultado.data.importe;
      return $http({method: 'GET',url: 'fichero2.json'})
    }).then(function(resultado) {
      $scope.importeTotalPromesas = $scope.importeTotalPromesas + resultado.data.importe;
      return $http({method: 'GET',url: 'fichero3.json'})
    }).then(function(resultado) {
      $scope.importeTotalPromesas = $scope.importeTotalPromesas + resultado.data.importe;
      return $http({method: 'GET',url: 'fichero4.json'})
    }).then(function(resultado) {
      $scope.importeTotalPromesas = $scope.importeTotalPromesas + resultado.data.importe;
      $scope.mensajeFinalPromesas = "Ya hemos finalizado la lista de cálculos con promesas";
    })

Aunque aún no hemos explicado cómo funcionan las promesas, vemos en este ejemplo que ahora no hay 4 funciones independientes y que no están anidadas por lo que es mas sencillo separarlas en funciones independientes.

Otras ventajas

Siempre que se habla de las promesas se explica que resuelven el problema de la pirámide de la muerte pero desde mi punto de vista tienen otras ventajas que las hacen también útiles:

API Unificado

En el entorno de JavaScript se produce mucho el fenómeno de llamadas asíncronas y el consiguiente uso de callbacks. Éso lleva a que tengamos de definir esos callbacks. Aunque puede parecer una tarea sencilla realmente hay muchas formas de hacer los callbacks. Veamos algunos ejemplos:

Para poner las distintas formas , vamos a suponer lo siguiente.

  • Un método llamado “llamadaAsincrona” al que le pasamos un parámetro con el valor “EURO”
  • Si todo funciona correctamente , una función de callback con dos argumentos con los datos llamados ret1 y ret2
  • Si algo falla, una función de callback llamada errorCallback con dos argumentos de error llamados codError y mensajeError.

llamadaAsincrona("EURO",function (ret1,ret2) {
   alert(ret1 + "," + ret2);
},function(codError,mensajeError) {
   alert(codError + "," + mensajeError);
});
En la propia llamada se pasan las 2 funciones de callback

llamadaAsincrona("EURO").sucess(function (ret1,ret2) {
   alert(ret1 + "," + ret2);
}).error(function(codError,mensajeError) {
   alert(codError + "," + mensajeError);
});
Ahora la función llamadaAsincrona retorna un objeto que tiene los métodos success y error a los que le pasamos a cada uno la función correspondiente.

llamadaAsincrona("EURO").then(function (ret1,ret2) {
   alert(ret1 + "," + ret2);
},function(codError,mensajeError) {
   alert(codError + "," + mensajeError);
});

Ahora se retorna un objeto con un método then al que ahora se le pasan las 2 funciones.

llamadaAsincrona("EURO").then(function (data) {
   alert(data.ret1 + "," + data.ret2);
},function(error) {
   alert(error.codError + "," + error.mensajeError);
});
Este caso es como el anterior pero en vez de pasar 2 argumentos en las funciones de callback sólo se pasa un solo objeto que tiene como propiedades los 2 valores.

Como vemos, hay muchas formas de hacer las funciones de callback. Usando el API de promesas de AngularJS en nuestro código unificaremos la forma de trabajar de toda la aplicación.

Hay que decir que el servicio $http no es exactamente una promesa por lo que el propio AngularJS no ha pretendido tener un sólo API. El motivo de ello supongo que habrá sido el facilitar el uso del servicio de $http pero personalmente creo que ha sido un error. Yo hubiera obligado a usar siempre todo como promesas y no añadir métodos adicionales para facilitar su uso.

Resueltos antes de llamar al controlador

Lo que vamos a explicar ahora es para mí la mayor ventaja de las promesas y es el verdadero motivo por el que las explico ahora en vez de dejarlas como algo avanzado para el final.

Hemos visto en los controladores cómo hacemos llamadas a $http 1) para obtener los datos. Éso tiene un problema, la página ya se ha mostrado pero aún estamos esperando los datos. Lo ideal sería que el controlador tuviera los datos que necesita antes de mostrarse la página y antes de ejecutarse el controlador. De esa forma no veríamos la página con datos en blanco esperando la respuesta. Gracias a las promesas y al servicio de rutas de AngularJS ésto es posible.

No vamos ahora a contar el sistema de rutas ni cómo funciona. Eso lo dejamos para la próxima unidad 2) pero sólo repetir que gracias a las promesas podemos tener datos que se obtienen de forma asíncrona pero que llegarán al controlador antes de que éste se ejecute, por lo que será como si fuera algo síncrono.

Ejemplo

Referencias

1) aunque sea a través de servicios
2) Exactamente en el tema de Resolve
unidades/06_promesas/01_justificacion.txt · Última modificación: 2014/08/30 11:13 por admin
Ir hasta arriba
CC Attribution-Share Alike 3.0 Unported
chimeric.de = chi`s home Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0
Esta web utiliza cookies, puedes ver nuestra la política de cookies, aquí Si continuas navegando estás aceptándola
Política de cookies +