¡Esta es una revisión vieja del documento!


6.3 Promesas avanzadas

Acabamos de ver como funcionan las promesas. Para muchas aplicaciones son solo eso será necesario sin embargo AngularJS proporciona más métodos que nos pueden ser útiles en ciertos casos. Pasemos ahora a ver mas funcionalidades de las promesas.

Lo primero que vamos a hacer es actualizar el diagrama de clases de UML para reflejar todos los nuevos métodos que vamos a explicar.

PlantUML Graph

Notificaciones

Es posible que mientras la función asincrona a la que hemos llamado está calculando el resultado nos pueda notificar su propio progreso. Para ello la clase defered tiene otro método llamado notify al que le pasamos un valor. Este valor puede ser recogido desde la promesa añadiendo una tercera función de callback al método then de promise.

El diagrama de estados de la promesa ahora se modifica de la siguiente forma:

PlantUML Graph

Vemos ahora que se puede llamar a defered.notify() todas las veces que se quiera que la promesa seguirá en el estado Pendiente.

No es posible llamar a defered.notify() una vez se haya resuelto la promesa pero tampoco antes de que se llame al método promise.then.Esto último implica que no se puede llamar dentro de la función antes de retornar el objeto de la clase Promise.

El ejemplo del tema anterior lo hemos modificado para incluir un nuevo texto con el % de progreso de la operación.

<!DOCTYPE html>
<html ng-app="app">
  <head>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js"></script>
    <script src="//code.angularjs.org/1.2.19/i18n/angular-locale_es-es.js"></script>
    <script src="script.js"></script>
  </head>
  <body ng-controller="PruebaController">
    {{mensaje}}
    <br>
    {{progreso}}
  </body>
</html>

  • Línea 11: Mostramos el % de progreso de la operación.

    function sumaAsincrona(a, b) {
      var defered = $q.defer();
      var promise = defered.promise;


      $timeout(function() {
        defered.notify(0);
      }, 1);
      $timeout(function() {
        defered.notify(33);
      }, 1000);
      $timeout(function() {
        defered.notify(66);
      }, 2000);
      
      $timeout(function() {
        try {
          var resultado = a + b;
          defered.notify(100);
          defered.resolve(resultado);
        } catch (e) {
          defered.reject(e);
        }
      }, 3000);
      return promise;
    }

    var promise = sumaAsincrona(5, 2);

    promise.then(function(resultado) {
      $scope.mensaje = "El resultado de la promesa es:" + resultado;
    }, function(error) {
      $scope.mensaje = "Se ha producido un error al obtener el dato:" + error;
    }, function(progreso) {
      $scope.progreso = progreso+"%";
    });

  • Lineas 6,7 y 8: Se ha puesto un timeout para que al pasar un milisegundo se notifique que vamos por el 0% 1).
  • Líneas 9,10 y 11: Se notifica que vamos por el 33% al pasar 1000 ms
  • Líneas 12,13 y 14: Se notifica que vamos por el 66% al pasar 2000 ms
  • Línea 19: Ya hemos acabado los cálculos y notificamos que vamos por el 100%. Recuerda que esta línea hay que ponerla antes de defered.resolve ya que sino nunca se notificaría.
  • Líneas 34 y 35: Se añade la tercera función al método then la cual será llamada cada vez que nos notifiquen algo sobre el progreso de la operación. En este caso añadimos el carácter ”%” y lo ponemos en el %scope para que se muestre en la página HTML.

Finalizador

Hay veces que nos interesa que se ejecute un código cuando se resuelve la promesa independientemente de si ha sido resuelta o rechazada. Para ello la clase Promise dispone de un método llamado finally que acepta como argumento una función de callback. Esta función de callback se llamará justo antes de llamar a las funciones definidas en el método then.

Debido a que el método se llama finally y eso es una palabra reservada en javaScript, puede que el navegador no te deje llamarla directamente así que se deberá llamar usando la siguiente forma:

    promise["finally"](function() {
        alert("Mensaje del ejemplo.Esto se llama justo antes de resolver o rechazar la promesa:");
    });

  • Línea 2: Se ejecutará justo antes de resolver o rechazar la promesa , por lo que siempre 2) se ejecutará.

Hacer una promesa de un valor

Otro caso que puede ocurrir es que la función a la que llamamos en vez de tener que calcular asíncronamente el resultado ya lo tenga disposible. Un caso muy típico en una llamada $http en el que el resultado está cacheado, en ese caso no tendría sentido usar una promesa ya que ya tenemos el resultado en el momento de llamar a la función. Sin embargo como el interfaz de nuestra función no lo podemos modificar , es necesario seguir usando una promesa.

Para estos casos AngularJS no ofrece el método $q.when que permite transforma cualquier valor en una promesa.

Siguiendo no nuestro ejemplo hemos modificado la función sumaAsincrona para que cuando ambos argumentos son cero, no sea necesaria hacer el “calculo” sino directamente retornamos el cero.

      if ((a===0) && (b===0)) {
        return $q.when(0);
      }

  • Línea 2: Retornamos una promesa cuyo resultado ya es el cero.

Juntar varias promesas

Otra método útil de $q es all. Este método permite retornar una única promesa que unifica a varias promesas. Esto se utiliza para cuando queremos que varias promesas se ejecuten en paralelo y queremos esperar hasta que todas ellas estén resueltas.

Hay 2 formas de hacer ésto:

Array de promesas

Lo que hacemos es obtener todas las promesas y añadirlas todas a un array. Entonces llamamos al método $q.all(array) y le pasamos el array que contiene todas las promesas. Este método nos retornará la promesa unificada la cual solo se resolverá si todas se resuelven. Si alguna de las promesas iniciales es rechazada, la promesa unificada también será rechazada.

Al haber varias promesas la forma de funcionar cambia de la siguiente forma:

  • Al resolver la promesa, el parámetro es un array con cada uno de los valores.
  • Si alguna de las promesas iniciales es rechazada , a la promesa unificada solo le llega un único rechazo que será el de la primera promesa que haya sido rechazada. El resto de los rechazos no se notificarán.
  • No se generan mensajes de notificación en la promesa unificada.
  • Si alguno de los elementos del array de promesas no es una promesa , entonces se llamará a $q.when para transformar el valor en una promesa.

En el siguiente ejemplo podemos ver como se unifican varias promesas en una sola usando un array:

    $scope.mensajeMultipleArray = "Esperando a una promesa múltiple formada por muchas promesas en forma de Array";
    var promesaMultipleArray=$q.all([sumaAsincrona(0,0),sumaAsincrona(1,0),sumaAsincrona(2,0)]);
    
    promesaMultipleArray.then(function(resultado) {
      $scope.mensajeMultipleArray = "El resultado de la promesa múltiple formada por muchas promesas en forma de Array es:" + resultado[0] + "," + resultado[1] + "," + resultado[2];
    }, function(error) {
      $scope.mensajeMultipleArray ="Se ha producido un error en alguna de las multiples promesas:"+error;
    }, function(progreso) {
      $scope.progresoMultipleArray = progreso+"%";
    });

  • Línea 2: Creamos una nueva promesa llamada promesaMultiple que es un array de la unión de las 3 promesas .
  • Línea 5: El argumento resultado es un array con los 3 resultados de las 3 promesas.
  • Línea 7: Solo será llamado una única vez para la primera promesa que sea rechazada.
  • Línea 9: Nunca se llamará a este código ya que nunca se notifica nada en las promesas múltiples.

Objeto con promesas

Lo que hacemos es obtener todas las promesas y añadirlas todas a un objeto, donde cada propiedad del objeto debe ser una promesa. Entonces llamamos al método $q.all(objeto) y le pasamos el objeto que contiene las promesas. Este método nos retornará la promesa unificada la cual solo se resolverá si todas se resuelven. Si alguna de las promesas iniciales es rechazada, la promesa unificada también será rechazada.

Al haber varias promesas la forma de funcionar cambia de la siguiente forma:

  • Al resolver la promesa, el parámetro es un objeto cuyas propiedades son cada uno de los valores.
  • Si alguna de las promesas iniciales es rechazada , a la promesa unificada solo le llega un único rechazo que será el de la primera promesa que haya sido rechazada. El resto de los rechazos no se notificarán.
  • No se generan mensajes de notificación en la promesa unificada.
  • Si alguno de las propiedades del objeto de promesas no es una promesa , entonces se llamará a $q.when para transformar el valor en una promesa.

En el siguiente ejemplo podemos ver como se unifican varias promesas en una sola usando un objeto:

    $scope.mensajeMultipleObjetos = "Esperando a una promesa múltiple formada por muchas promesas en forma de Objeto";
    var promesaMultipleObjetos=$q.all({
      promesaA : sumaAsincrona(0,0),
      promesaB : sumaAsincrona(1,0),
      promesaC : sumaAsincrona(2,0)
    });
    
    promesaMultipleObjetos.then(function(resultado) {
      $scope.mensajeMultipleObjetos = "El resultado de la promesa múltiple formada por muchas promesas en forma de Objeto es:" + resultado.promesaA + "," + resultado.promesaB + "," + resultado.promesaC;
    }, function(error) {
      $scope.mensajeMultipleObjetos ="Se ha producido un error en alguna de las multiples promesas:"+error;
    }, function(progreso) {
      $scope.progresoMultipleObjetos = progreso+"%";
    });

  • Línea 2: Creamos una nueva promesa llamada mensajeMultipleObjetos que es la unión de las 3 promesas. Cada promesa es un propiedad del objeto que hemos creado.
  • Línea 9: El argumento resultado es un objeto con los 3 resultados de las 3 promesas. Cada propiedad coincide con el nombre de la propiedad del objeto que contenía las promesas.
  • Línea 11: Solo será llamado una única vez para la primera promesa que sea rechazada.
  • Línea 13: Nunca se llamará a este código ya que nunca se notifica nada en las promesas múltiples.

Cadena de promesas

Ejemplo

El ejemplo contiene llamadas a todas los los métodos que hemos visto en este tema por lo que el código JavaScript ha quedado un poco largo.

Referencias

1) Esto se hace ya que no se puede llamar a notify antes de retornar el objeto de la clase Promise
2) siempre y cuando esté bien programado y siempre se resuelva o rechaze la promesa
unidades/06_promesas/03_avanzado.1408967064.txt.gz · Última modificación: 2014/08/25 13:44 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