Muestra las diferencias entre dos versiones de la página.
unidades:06_promesas:02_q [2014/08/16 00:12] admin [6.2 Servicio $q] |
unidades:06_promesas:02_q [2014/09/01 23:53] (actual) admin |
||
---|---|---|---|
Línea 1: | Línea 1: | ||
====== 6.2 Servicio $q ====== | ====== 6.2 Servicio $q ====== | ||
- | El servicio de [[https://docs.angularjs.org/api/ng/service/$q|$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 [[http://documentup.com/kriskowal/q|Kris Kowal's Q.]]. AngularJS ha hecho su propia versión para que esté todo integrado con el propio framework. | + | El servicio de [[https://docs.angularjs.org/api/ng/service/$q|$q]] es un servicio de AngularJS que contiene toda la funcionalidad de las promesas. Tal y como se indica en su documentación, está basado en la implementación de [[http://documentup.com/kriskowal/q|Kris Kowal's Q.]]. AngularJS ha hecho su propia versión para que esté todo integrado en el propio framework. |
- | El sistema de promesas yo lo suelo comparar al [[http://es.wikipedia.org/wiki/Problema_Productor-Consumidor|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. | + | Yo suelo comparar el sistema de promesas al [[http://es.wikipedia.org/wiki/Problema_Productor-Consumidor|problema del productor-consumidor]]. La similitud es 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 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: | ||
<uml> | <uml> | ||
Línea 40: | Línea 42: | ||
- | Vamos a hacer un ejemplo de una función que acepta como parámetro dos valores y de forma asíncrona los retorna. | + | Una vez creada mediante el método ''defer()'', una promesa se puede encontrar en alguno de los siguiente 3 estados: |
+ | |||
+ | <uml> | ||
+ | [*] -d-> Pendiente : $q.defer() | ||
+ | Pendiente -d-> Resuelta : defered.resolve() | ||
+ | Pendiente -d-> Rechazada : defered.reject() | ||
+ | Resuelta -d-> [*] | ||
+ | Rechazada -d-> [*] | ||
+ | </uml> | ||
+ | |||
+ | * **Pendiente**: Aún no se sabe si se podrá o no obtener el resultado. | ||
+ | * **Resulta**: Se ha podido obtener el resultado. Se llega a este estado llamando al método ''defered.resolve()'' | ||
+ | * **Rechazada**: Ha habido algún tipo de error y no se ha podido obtener el resultado. Se llega a este estado llamando al método ''defered.reject()'' | ||
+ | |||
+ | Debido a que la promesa debe acabar en el estado Pendiente o Rechazado es obligatorio que siempre llamemos al método ''defered.resolve()'' o ''defered.reject()''. | ||
+ | |||
+ | |||
+ | ===== $q.defer() ===== | ||
+ | Para explicar las promesas vamos a hacer un ejemplo de una función que acepta como parámetro dos valores y de forma asíncrona los retorna. Obviamente para hacer una simple suma no es necesario usar promesas ni retornarlo de forma asíncrona, pero servirá como ejemplo. | ||
+ | |||
+ | Lo primero es crear los 2 objetos que se necesitan en las promesas. | ||
<sxh js; highlight: [1,2,3,5]> | <sxh js; highlight: [1,2,3,5]> | ||
Línea 50: | Línea 72: | ||
} | } | ||
</sxh> | </sxh> | ||
+ | |||
* Línea 1: La función acepta como argumentos 2 números , llamados ''a'' y ''b''. | * 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 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 55: | Línea 78: | ||
* Línea 5: Al que nos llama le devolvemos la promesa. | * 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. | + | Ya tenemos los 2 objetos preparados y listos para ser usados. Ahora explicaremos más sobre cada uno de ellos. |
===== defered ===== | ===== 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 ((aunque realmente no la produce sino que solo notifica si se ha producido o no)) | + | El objeto ''defered'' sólo 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 ((aunque realmente no la produce sino que sólo notifica si se ha producido o no)). |
- | 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. | + | Este objeto tiene 2 métodos. Uno de ellos para indicar que se ha obtenido la información y por lo tanto hacer que la promesa pase al estado "Resuelto" y un segundo método para indicar que algo ha fallado y que no se ha podido obtener la información y por lo tanto hacer que la promesa pase al estado "Rechazado". |
^ Método ^ Parámetros ^ Descripció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. | | + | | ''resolve '' | ''resolve(resultado)'' | Llamaremos a este método para indicar que ya tenemos la información que se solicitó y por lo tanto que la promesa está resuelta, 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. | | + | | ''reject'' | ''reject(error)'' | Llamaremos a este método para indicar que no ha sido posible obtener la información que se solicitó y por lo tanto que la promesa está rechazada, 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. | + | |
- | <note tip>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.</note> | + | <note tip> |
+ | 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. Éso ya dependerá de cada uno de los métodos que creemos que usen promesas. Tampoco se permite retornar más de un dato. Si queremos retornar más de uno simplemente debemos crear un objeto que contenga la información que queramos. | ||
+ | </note> | ||
Sigamos ahora con el ejemplo y usemos el servicio de [[unidades:03_servicios:03_timeout|$timeout]] para simular una respuesta asíncrona de la función. | Sigamos ahora con el ejemplo y usemos el servicio de [[unidades:03_servicios:03_timeout|$timeout]] para simular una respuesta asíncrona de la función. | ||
- | |||
Línea 95: | Línea 117: | ||
* Línea 8: Indicamos que todo ha ido bien y retornamos el resultado. | * 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. | * 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 ===== | ===== 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. | + | 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 qué 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. | Lo primero que debemos hacer es llamar a la función asíncrona y guardarnos la promesa que nos retorna. | ||
Línea 107: | Línea 127: | ||
* Línea 1: Llamar a la función asíncrona y guardarnos la promesa que nos retorna. | * 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. | + | ¿Cómo podemos saber ahora si se ha podido o no obtener el dato? ¿Y cómo obtenemos el dato? El objeto ''promise'' tiene un método llamado ''then'', el cual acepta que le pasemos 2 funciones de //callback// para saber lo que ha ocurrido. La primera función se llamará si se ha obtenido la información y por lo tanto si la promesa ha sido resuelta. La segunda función ((que llamaremos función de fallo)) se llamará si algo ha fallado y por lo tanto si la promesa ha sido rechazada. |
<sxh js; highlight: [3,4,5,6,7]> | <sxh js; highlight: [3,4,5,6,7]> | ||
Línea 119: | Línea 139: | ||
</sxh> | </sxh> | ||
* Línea 3: Llamamos al método ''then'' del objeto ''promise'' y le pasamos las 2 funciones. | * 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 4: La promesa ha sido resuelta y 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 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. | + | * Línea 6: La promesa ha sido rechazada ya que algo ha fallado y en este caso generamos en mensaje de error. |
===== Ejemplo ===== | ===== Ejemplo ===== | ||
Línea 172: | Línea 192: | ||
</sxh> | </sxh> | ||
- | {{url>http://embed.plnkr.co/zVzpNroj8vn87LYwfYUj}} | + | <note tip> |
+ | Normalmente, para abreviar, en vez de guardarnos la variable de la promesa y luego llamar al método ''then'' se suele hacer todo junto de la siguiente manera: | ||
+ | <sxh js> | ||
+ | sumaAsincrona(5, 2).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; | ||
+ | }); | ||
+ | </sxh> | ||
+ | |||
+ | </note> | ||
+ | |||
+ | {{url>http://embed.plnkr.co/zVzpNroj8vn87LYwfYUj}} | ||
===== Referencias ===== | ===== Referencias ===== | ||
* [[https://docs.angularjs.org/api/ng/service/$q|$q]] | * [[https://docs.angularjs.org/api/ng/service/$q|$q]] |