¡Esta es una revisión vieja del documento!
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.
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:
Vemos ahora que se puede llamar a defered.notify()
todas las veces que se quiera que la promesa seguirá en el estado Pendiente
.
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>
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+"%"; });
defered.resolve
ya que sino nunca se notificaría.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.
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:"); });
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); }
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:
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 un array. 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:
En el siguiente ejemplo podemos ver como se unifican varias promesas en una sola
$scope.mensajeMultiple = "Esperando a una promesa múltiple formada por muchas promesas"; var promesaMultiple=$q.all([sumaAsincrona(0,0),sumaAsincrona(1,0),sumaAsincrona(2,0)]); promesaMultiple.then(function(resultado) { $scope.mensajeMultiple = "El resultado de la promesa múltiple formada por muchas promesas es:" + resultado; }, function(error) { $scope.mensajeMultiple ="Se ha producido un error en alguna de las multiples promesas:"+error; }, function(progreso) { $scope.progresoMultiple = progreso+"%"; });