¡Esta es una revisión vieja del documento!


6.2 Servicio $q

El servicio de $q es un servicio de AngularJS que contiene toda la funcionalidad de las promesas. Tal y como indican en su documentación está basado en la implementación de Kris Kowal's Q.. AngularJS ha hecho su propia versión para que esté todo integrado con el propio framework.

El sistema de promesas yo lo suelo comparar al problema del productor-consumidor. La similitud es en que hay una parte que generará la información , por ejemplo el método $http y otra parte que consumirá la información, por ejemplo nuestro código. Esta separación es importante ya que hay 2 objetos con los que tendremos que tratar.

En la nomenclatura de AngularJS al productor se le llama defered y al consumidor se le llama promise. Mediante el servicio de $q obtenemos el objeto defered llamando el método defer() y a partir de él obtenemos el objeto promise llamando a la propiedad promise

En el siguiente diagrama UML podemos ver las distintas clases que forman parte de las promesas: PlantUML Graph

Una promesa una vez creada mediante el método defer() se puede encontrar en 3 estados: PlantUML Graph

Vamos a hacer un ejemplo de una función que acepta como parámetro dos valores y de forma asíncrona los retorna.

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

  • Línea 1: La función acepta como argumentos 2 números , llamados a y b.
  • Línea 2: Usando el servicio de $q obtenemos el objeto defered llamando al métdo defer(). Por supuesto el servicio $q debe ser inyectado de alguna forma pero aquí lo hemos obviado para simplificar la explicación.
  • Línea 3: Desde el objeto defer se obtiene el objeto promise.
  • Línea 5: Al que nos llama le devolvemos la promesa.

Ya tenemos los 2 objetos preparados y listos para ser usados. Ahora explicaremos mas sobre cada uno de ellos.

defered

El objeto defered solo se usa desde dentro de la función asíncrona, por lo tanto el que llama a sumaAsincrona no sabe nada del objeto defered. Como ya hemos dicho el objeto defered hará las funciones de productor de la información 1)

Este objeto tiene de 2 métodos. Uno de ellos para indicar que se han obtenido la información y un segundo método para indicar que hago ha fallado y que no se ha podido obtener la información.

Método Parámetros Descripción
resolve resolve(resultado) Llamaremos a este método para indicar que ya tenemos la información que se solicitó. Siendo el parámetro resultado el que contiene la información solicitada.
reject reject(error) Llamaremos a este método para indicar que no ha sido posible obtener la información que se solicitó. Conteniendo el parámetro error información relativa a la naturaleza del error.

Hay que fijarse que el objeto defered no dice nada relativo al tipo de información que se retorna ni a la estructura de la misma. Eso ya dependerá de cada uno de los métodos que creemos que usen promesas.

Una promesa solo puede retornar un único dato, si queremos retornar mas de uno simplemente debemos crear un objeto que contenga la información que queramos.

Sigamos ahora con el ejemplo y usemos el servicio de $timeout para simular una respuesta asíncrona de la función.

function sumaAsincrona(a,b) {
   var defered=$q.defer();
   var promise=defered.promise;
   
   $timeout(function() {
      try{
         var resultado=a+b;
         defered.resolve(resultado);
      } catch (e) {
         defered.reject(e);
      }   
   },3000); 
   
   return promise;
}

  • Línea 5: Ponemos un timeout para que pasados 3 segundos 2) se ejecute la función.
  • Línea 7: Aqui es donde calculamos la información que sea necesaria. Lo único que hacemos es sumar los 2 valores que nos pasaron.
  • Línea 8: Indicamos que todo ha ido bien y retornamos el resultado.
  • Línea 10: Si algo ha fallado lo indicamos llamando a reject. En este caso pasamos como valor de retorno la propia excepción que se ha generado.

promise

Acabamos de ver lo que hay que hacer internamente para producir la información en la función sumaAsincrona, ahora pasemos al otro lado del problema. Veamos lo que ocurre al llamar a nuestra función asíncrona, es decir en la parte del consumidor.

Lo primero que debemos hacer es llamar a la función asíncrona y guardarnos la promesa que nos retorna.

var promise=sumaAsincrona(5,2);

  • Línea 1: Llamar a la función asíncrona y guardarnos la promesa que nos retorna.

¿Como nos enteramos ahora si se ha podido o no obtener el dato? ¿Y como obtenemos el dato? El objeto promise tiene un método llamado then, el cual acepta que le pasemos 2 funciones de callback para saber que ha ocurrido. La primera de ella se llamará si se ha obtenido la información y la segunda de ellas si algo ha fallado.

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;
});

  • Línea 3: Llamamos al método then del objeto promise y le pasamos las 2 funciones.
  • Línea 4: Aquí nos guardamos en el $scope el resultado que nos ha llegado a través de la promesa
  • Línea 5: Especificamos la función de error para cuando no se puede obtener la información.
  • Línea 6: En caso de que algo falle, generamos en mensaje de error.

Ejemplo

<!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}}
  </body>
</html>

var app = angular.module("app", []);

app.controller("PruebaController", ['$scope', '$q', '$timeout',function($scope, $q, $timeout) {

    $scope.mensaje = "Esperando a que se resuelva la promesa"

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

      $timeout(function() {
        try {
          var resultado = a + b;
          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;
    });

}]);

Referencias

1) aunque realmente no la produce sino que solo notifica si se ha producido o no
2) línea 12
unidades/06_promesas/02_q.1408178965.txt.gz · Última modificación: 2014/08/16 10:49 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