J'ai vu quelques exemples de services de connexion Facebook qui utilisaient des promesses pour accéder à l'API FB Graph.
Exemple n ° 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;
}
Et les services qui ont été utilisés "$scope.$digest() // Manual scope evaluation"
lors de la réponse
Exemple n ° 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
});
}
}
Les questions sont:
- Quelle est la différence entre les exemples ci-dessus?
- Quelles sont les raisons et les cas d'utilisation du service $ q ?
- Et comment ça marche ?
Réponses:
Cela ne sera pas une réponse complète à votre question, mais j'espère que cela vous aidera, vous et d'autres personnes, lorsque vous essayez de lire la documentation sur le
$q
service. Il m'a fallu un certain temps pour le comprendre.Laissons AngularJS de côté pendant un moment et considérons simplement les appels d'API Facebook. Les deux appels d'API utilisent un mécanisme de rappel pour notifier l'appelant lorsque la réponse de Facebook est disponible:
Il s'agit d'un modèle standard pour gérer les opérations asynchrones en JavaScript et dans d'autres langages.
Un gros problème avec ce modèle se pose lorsque vous devez effectuer une séquence d'opérations asynchrones, où chaque opération successive dépend du résultat de l'opération précédente. C'est ce que fait ce code:
Tout d'abord, il essaie de se connecter, puis ce n'est qu'après avoir vérifié que la connexion a réussi qu'il fait la demande à l'API Graph.
Même dans ce cas, qui n'enchaîne que deux opérations, les choses commencent à se compliquer. La méthode
askFacebookForAuthentication
accepte un rappel en cas d'échec et de réussite, mais que se passe-t-il lorsqueFB.login
réussit maisFB.api
échoue? Cette méthode appelle toujours lesuccess
rappel quel que soit le résultat de laFB.api
méthode.Imaginez maintenant que vous essayez de coder une séquence robuste de trois opérations asynchrones ou plus, d'une manière qui gère correctement les erreurs à chaque étape et sera lisible par n'importe qui d'autre ou même par vous après quelques semaines. Possible, mais il est très facile de continuer à imbriquer ces rappels et de perdre la trace des erreurs en cours de route.
Maintenant, laissons de côté l'API Facebook pendant un moment et considérons simplement l'API Angular Promises, telle qu'implémentée par le
$q
service. Le modèle implémenté par ce service est une tentative de transformer la programmation asynchrone en quelque chose qui ressemble à une série linéaire d'instructions simples, avec la possibilité de `` lancer '' une erreur à n'importe quelle étape du processus et de la gérer à la fin, sémantiquement similaire autry/catch
bloc familier .Considérez cet exemple artificiel. Disons que nous avons deux fonctions, où la deuxième fonction consomme le résultat de la première:
Imaginez maintenant que firstFn et secondFn prennent tous les deux du temps à se terminer, nous voulons donc traiter cette séquence de manière asynchrone. Nous créons d'abord un nouvel
deferred
objet, qui représente une chaîne d'opérations:La
promise
propriété représente le résultat final de la chaîne. Si vous enregistrez une promesse immédiatement après l'avoir créée, vous verrez qu'il ne s'agit que d'un objet vide ({}
). Rien à voir encore, avancez tout de suite.Jusqu'à présent, notre promesse ne représente que le point de départ de la chaîne. Ajoutons maintenant nos deux opérations:
La
then
méthode ajoute une étape à la chaîne, puis retourne une nouvelle promesse représentant le résultat final de la chaîne étendue. Vous pouvez ajouter autant d'étapes que vous le souhaitez.Jusqu'à présent, nous avons mis en place notre chaîne de fonctions, mais rien ne s'est réellement passé. Vous démarrez les choses en appelant
deferred.resolve
, en spécifiant la valeur initiale que vous souhaitez passer à la première étape réelle de la chaîne:Et puis ... il ne se passe toujours rien. Pour s'assurer que les changements de modèle sont correctement observés, Angular n'appelle pas réellement la première étape de la chaîne jusqu'à ce que la prochaine fois
$apply
soit appelée:Alors qu'en est-il de la gestion des erreurs? Jusqu'à présent, nous n'avons spécifié qu'un gestionnaire de succès à chaque étape de la chaîne.
then
accepte également un gestionnaire d'erreurs comme second argument facultatif. Voici un autre exemple plus long de chaîne de promesses, cette fois avec gestion des erreurs:Comme vous pouvez le voir dans cet exemple, chaque gestionnaire de la chaîne a la possibilité de détourner le trafic vers le prochain gestionnaire d' erreur au lieu du prochain succès gestionnaire de . Dans la plupart des cas, vous pouvez avoir un seul gestionnaire d'erreurs à la fin de la chaîne, mais vous pouvez également avoir des gestionnaires d'erreurs intermédiaires qui tentent de récupérer.
Pour revenir rapidement à vos exemples (et à vos questions), je dirai simplement qu'ils représentent deux façons différentes d'adapter l'API orientée callback de Facebook à la façon dont Angular observe les changements de modèle. Le premier exemple encapsule l'appel d'API dans une promesse, qui peut être ajoutée à une portée et est comprise par le système de modèles d'Angular. Le second adopte l'approche plus brutale consistant à définir le résultat du rappel directement sur la portée, puis à appeler
$scope.$digest()
pour informer Angular du changement depuis une source externe.Les deux exemples ne sont pas directement comparables, car le premier ne comprend pas l'étape de connexion. Cependant, il est généralement souhaitable d'encapsuler les interactions avec des API externes comme celle-ci dans des services séparés et de fournir les résultats aux contrôleurs comme des promesses. De cette façon, vous pouvez séparer vos contrôleurs des préoccupations externes et les tester plus facilement avec des services simulés.
la source
then
méthodes consiste à utiliser$q.all
. Un tutoriel rapide à ce sujet peut être trouvé ici .$q.all
convient si vous devez attendre la fin de plusieurs opérations asynchrones indépendantes . Il ne remplace pas le chaînage si chaque opération dépend du résultat de l'opération précédente.return 'firstResult'
pièce enreturn $q.resolve('firstResult')
, quelle sera la différence?C'est le but des promesses angulaires MVP (promesse viable minimum) : http://plnkr.co/edit/QBAB0usWXc96TnxqKhuA?p=preview
La source:
(pour ceux qui sont trop paresseux pour cliquer sur les liens)
index.html
app.js
(Je sais que cela ne résout pas votre exemple Facebook spécifique, mais je trouve les extraits suivants utiles)
Via: http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/
Mise à jour du 28 février 2014: à partir de la version 1.2.0, les promesses ne sont plus résolues par les modèles. http://www.benlesh.com/2013/02/angularjs-creating-service-with-http.html
(L'exemple plunker utilise 1.1.5.)
la source
Un différé représente le résultat d'une opération asynchrone. Il expose une interface qui peut être utilisée pour signaler l'état et le résultat de l'opération qu'il représente. Il fournit également un moyen d'obtenir l'instance de promesse associée.
Une promesse fournit une interface pour interagir avec sa relation différée, et permet ainsi aux parties intéressées d'accéder à l'état et au résultat de l'opération différée.
Lors de la création d'un différé, son état est en attente et il n'a aucun résultat. Lorsque nous résolvons () ou rejetons () le différé, il change son état en résolu ou rejeté. Néanmoins, nous pouvons obtenir la promesse associée immédiatement après la création d'un différé et même attribuer des interactions avec son résultat futur. Ces interactions n'auront lieu qu'après le rejet ou la résolution du différé.
la source
utiliser la promesse au sein d'un contrôleur et s'assurer que les données sont disponibles ou non
la source