Vi algunos ejemplos de servicios de inicio de sesión de Facebook que usaban promesas para acceder a FB Graph API.
Ejemplo # 1 :
this.api = function(item) {
var deferred = $q.defer();
if (item) {
facebook.FB.api('/' + item, function (result) {
$rootScope.$apply(function () {
if (angular.isUndefined(result.error)) {
deferred.resolve(result);
} else {
deferred.reject(result.error);
}
});
});
}
return deferred.promise;
}
Y los servicios que se usaron "$scope.$digest() // Manual scope evaluation"
cuando obtuvieron la respuesta
Ejemplo # 2 :
angular.module('HomePageModule', []).factory('facebookConnect', function() {
return new function() {
this.askFacebookForAuthentication = function(fail, success) {
FB.login(function(response) {
if (response.authResponse) {
FB.api('/me', success);
} else {
fail('User cancelled login or did not fully authorize.');
}
});
}
}
});
function ConnectCtrl(facebookConnect, $scope, $resource) {
$scope.user = {}
$scope.error = null;
$scope.registerWithFacebook = function() {
facebookConnect.askFacebookForAuthentication(
function(reason) { // fail
$scope.error = reason;
}, function(user) { // success
$scope.user = user
$scope.$digest() // Manual scope evaluation
});
}
}
Las preguntas son:
- ¿Cuál es la diferencia en los ejemplos anteriores?
- ¿Cuáles son las razones y los casos para usar el servicio $ q ?
- Y cómo funciona ?
Respuestas:
Esta no será una respuesta completa a su pregunta, pero espero que esto lo ayude a usted y a otros cuando intente leer la documentación del
$q
servicio. Me tomó un tiempo entenderlo.Dejemos de lado AngularJS por un momento y solo consideremos las llamadas API de Facebook. Ambas llamadas API utilizan un mecanismo de devolución de llamada para notificar a la persona que llama cuando la respuesta de Facebook está disponible:
Este es un patrón estándar para manejar operaciones asincrónicas en JavaScript y otros idiomas.
Un gran problema con este patrón surge cuando necesita realizar una secuencia de operaciones asincrónicas, donde cada operación sucesiva depende del resultado de la operación anterior. Eso es lo que está haciendo este código:
Primero intenta iniciar sesión, y solo después de verificar que el inicio de sesión se realizó correctamente, realiza la solicitud a la API Graph.
Incluso en este caso, que solo está encadenando dos operaciones, las cosas comienzan a complicarse. El método
askFacebookForAuthentication
acepta una devolución de llamada por falla y éxito, pero ¿qué sucede cuandoFB.login
tiene éxito peroFB.api
falla? Este método siempre invoca lasuccess
devolución de llamada independientemente del resultado delFB.api
método.Ahora imagine que está tratando de codificar una secuencia robusta de tres o más operaciones asincrónicas, de una manera que maneje adecuadamente los errores en cada paso y sea legible para cualquier otra persona o incluso para usted después de unas pocas semanas. Es posible, pero es muy fácil seguir anidando esas devoluciones de llamada y perder la noción de errores en el camino.
Ahora, dejemos de lado la API de Facebook por un momento y consideremos la API de Promesas Angulares, tal como la implementa el
$q
servicio. El patrón implementado por este servicio es un intento de convertir la programación asincrónica en algo parecido a una serie lineal de declaraciones simples, con la capacidad de 'arrojar' un error en cualquier paso del camino y manejarlo al final, semánticamente similar altry/catch
Bloque familiar .Considere este ejemplo artificial. Digamos que tenemos dos funciones, donde la segunda función consume el resultado de la primera:
Ahora imagine que firstFn y secondFn tardan mucho tiempo en completarse, por lo que queremos procesar esta secuencia de forma asincrónica. Primero creamos un nuevo
deferred
objeto, que representa una cadena de operaciones:La
promise
propiedad representa el resultado final de la cadena. Si registra una promesa inmediatamente después de crearla, verá que es solo un objeto vacío ({}
). Aún no hay nada que ver, muévete.Hasta ahora, nuestra promesa solo representa el punto de partida de la cadena. Ahora agreguemos nuestras dos operaciones:
El
then
método agrega un paso a la cadena y luego devuelve una nueva promesa que representa el resultado final de la cadena extendida. Puede agregar tantos pasos como desee.Hasta ahora, hemos configurado nuestra cadena de funciones, pero en realidad no ha pasado nada. Empiezas las cosas llamando
deferred.resolve
, especificando el valor inicial que deseas pasar al primer paso real de la cadena:Y entonces ... todavía no pasa nada. Para garantizar que los cambios en el modelo se observen correctamente, Angular en realidad no llama al primer paso en la cadena hasta que
$apply
se llame la próxima vez :Entonces, ¿qué pasa con el manejo de errores? Hasta ahora solo hemos especificado un controlador de éxito en cada paso de la cadena.
then
También acepta un controlador de errores como un segundo argumento opcional. Aquí hay otro ejemplo más largo de una cadena de promesa, esta vez con manejo de errores:Como puede ver en este ejemplo, cada controlador de la cadena tiene la oportunidad de desviar el tráfico al siguiente controlador de errores en lugar del siguiente controlador de éxito . En la mayoría de los casos, puede tener un único controlador de errores al final de la cadena, pero también puede tener controladores de errores intermedios que intenten la recuperación.
Para volver rápidamente a sus ejemplos (y sus preguntas), solo diré que representan dos formas diferentes de adaptar la API orientada a la devolución de llamadas de Facebook a la forma en que Angular observa los cambios en el modelo. El primer ejemplo envuelve la llamada API en una promesa, que se puede agregar a un alcance y se entiende por el sistema de plantillas de Angular. El segundo adopta el enfoque de fuerza más bruta de establecer el resultado de devolución de llamada directamente en el alcance y luego llamar
$scope.$digest()
para que Angular sea consciente del cambio desde una fuente externa.Los dos ejemplos no son directamente comparables, porque al primero le falta el paso de inicio de sesión. Sin embargo, generalmente es deseable encapsular interacciones con API externas como esta en servicios separados y entregar los resultados a los controladores como promesas. De esa manera, puede mantener sus controladores separados de las preocupaciones externas y probarlos más fácilmente con servicios simulados.
fuente
then
métodos es usar$q.all
. Un tutorial rápido sobre eso se puede encontrar aquí .$q.all
es apropiado si necesita esperar a que se completen varias operaciones asincrónicas independientes . No reemplaza el encadenamiento si cada operación depende del resultado de la operación anterior.return 'firstResult'
parte areturn $q.resolve('firstResult')
, ¿cuál será la diferencia?Este es el punto clave para las promesas angulares MVP (promesa mínima viable) : http://plnkr.co/edit/QBAB0usWXc96TnxqKhuA?p=preview
Fuente:
(para aquellos demasiado flojos para hacer clic en los enlaces)
index.html
app.js
(Sé que no resuelve su ejemplo específico de Facebook, pero creo que los siguientes fragmentos son útiles)
Vía: http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/
Actualización 28 de febrero de 2014: a partir de 1.2.0, las promesas ya no se resuelven mediante plantillas. http://www.benlesh.com/2013/02/angularjs-creating-service-with-http.html
(el ejemplo de plunker usa 1.1.5.)
fuente
Un diferido representa el resultado de una operación asincrónica. Expone una interfaz que se puede utilizar para señalar el estado y el resultado de la operación que representa. También proporciona una forma de obtener la instancia de promesa asociada.
Una promesa proporciona una interfaz para interactuar con sus diferidos relacionados y, por lo tanto, permite que las partes interesadas tengan acceso al estado y al resultado de la operación diferida.
Al crear un diferido, su estado está pendiente y no tiene ningún resultado. Cuando resolvemos () o rechazamos () lo diferido, cambia su estado a resuelto o rechazado. Aún así, podemos obtener la promesa asociada inmediatamente después de crear un resultado diferido e incluso asignar interacciones con su resultado futuro. Esas interacciones ocurrirán solo después de que el aplazado sea rechazado o resuelto.
fuente
use la promesa dentro de un controlador y asegúrese de que los datos estén disponibles o no
fuente