AngularJS: waar beloften gebruiken?

Ik zag enkele voorbeelden van Facebook Login-services die beloftengebruikten om toegang te krijgen tot de FB Graph API.

Voorbeeld #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;
}

En services die "$scope.$digest() // Manual scope evaluation"gebruikten toen ze het antwoord kregen

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

JSFiddle

De vragen zijn:

  • Wat is het verschilin de bovenstaande voorbeelden?
  • Wat zijn de redenenen gevallenom de service $qte gebruiken?
  • En hoe werkt het werk?

Antwoord 1, autoriteit 100%

Dit is geen volledig antwoord op uw vraag, maar hopelijk helpt dit u en anderen wanneer u de documentatie over de $q-service probeert te lezen. Het duurde even voordat ik het begreep.

Laten we AngularJS even opzij zetten en alleen de Facebook API-aanroepen beschouwen. Beide API-aanroepen gebruiken een callback-mechanisme om de beller op de hoogte te stellen wanneer het antwoord van Facebook beschikbaar is:

 facebook.FB.api('/' + item, function (result) {
    if (result.error) {
      // handle error
    } else {
      // handle success
    }
  });
  // program continues while request is pending
  ...

Dit is een standaardpatroon voor het afhandelen van asynchrone bewerkingen in JavaScript en andere talen.

Een groot probleem met dit patroon doet zich voor wanneer u een reeks asynchrone bewerkingen moet uitvoeren, waarbij elke volgende bewerking afhankelijk is van het resultaat van de vorige bewerking. Dat is wat deze code doet:

 FB.login(function(response) {
      if (response.authResponse) {
          FB.api('/me', success);
      } else {
          fail('User cancelled login or did not fully authorize.');
      }
  });

Eerst probeert het in te loggen, en pas nadat het is geverifieerd dat het inloggen is gelukt, wordt het verzoek ingediend bij de Graph API.

Zelfs in dit geval, dat slechts twee bewerkingen aan elkaar koppelt, begint het rommelig te worden. De methode askFacebookForAuthenticationaccepteert een callback voor mislukking en succes, maar wat gebeurt er als FB.loginslaagt maar FB.apifaalt? Deze methode roept altijd de successcallback aan, ongeacht het resultaat van de FB.apimethode.

Stel je nu voor dat je een robuuste reeks van drie of meer asynchrone bewerkingen probeert te coderen, op een manier die fouten bij elke stap correct afhandelt en na een paar weken leesbaar is voor iemand anders of zelfs voor jou. Mogelijk, maar het is heel gemakkelijk om die callbacks gewoon te nesten en onderweg fouten uit het oog te verliezen.

Laten we nu even de Facebook API opzij zetten en de Angular Promises API beschouwen, zoals geïmplementeerd door de $q-service. Het patroon dat door deze service wordt geïmplementeerd, is een poging om asynchrone programmering terug te zetten in iets dat lijkt op een lineaire reeks eenvoudige instructies, met de mogelijkheid om bij elke stap een fout te ‘gooien’ en deze aan het einde af te handelen, semantisch vergelijkbaar met de bekend try/catch-blok.

Beschouw dit gekunstelde voorbeeld. Stel dat we twee functies hebben, waarbij de tweede functie het resultaat van de eerste verbruikt:

var firstFn = function(param) {
    // do something with param
    return 'firstResult';
 };
 var secondFn = function(param) {
    // do something with param
    return 'secondResult';
 };
 secondFn(firstFn()); 

Stel je nu voor dat firstFn en secondFn beide veel tijd in beslag nemen, dus we willen deze reeks asynchroon verwerken. Eerst maken we een nieuw deferred-object, dat een reeks bewerkingen vertegenwoordigt:

var deferred = $q.defer();
 var promise = deferred.promise;

De eigenschap promisevertegenwoordigt het uiteindelijke resultaat van de keten. Als u een belofte onmiddellijk na het maken ervan vastlegt, ziet u dat het slechts een leeg object is ({}). Nog niets te zien, ga zo door.

Tot nu toe vormt onze belofte slechts het startpunt in de keten. Laten we nu onze twee bewerkingen toevoegen:

promise = promise.then(firstFn).then(secondFn);

De methode thenvoegt een stap toe aan de keten en retourneert vervolgens een nieuwe belofte die het uiteindelijke resultaat van de uitgebreide keten vertegenwoordigt. Je kunt zoveel stappen toevoegen als je wilt.

Tot nu toe hebben we onze keten van functies opgezet, maar er is eigenlijk niets gebeurd. U begint door deferred.resolveaan te roepen en de initiële waarde op te geven die u wilt doorgeven aan de eerste daadwerkelijke stap in de keten:

deferred.resolve('initial value');

En dan… gebeurt er nog steeds niets. Om ervoor te zorgen dat modelwijzigingen goed worden waargenomen, roept Angular de eerste stap in de keten pas aan als de volgende keer dat $applywordt aangeroepen:

deferred.resolve('initial value');
 $rootScope.$apply();
 // or     
 $rootScope.$apply(function() {
    deferred.resolve('initial value');
 });

Dus hoe zit het met foutafhandeling? Tot nu toe hebben we alleen een succes-handlergespecificeerd bij elke stap in de keten. thenaccepteert ook een fout-handler als een optioneel tweede argument. Hier is nog een langer voorbeeld van een belofteketen, dit keer met foutafhandeling:

var firstFn = function(param) {
    // do something with param
    if (param == 'bad value') {
      return $q.reject('invalid value');
    } else {
      return 'firstResult';
    }
 };
 var secondFn = function(param) {
    // do something with param
    if (param == 'bad value') {
      return $q.reject('invalid value');
    } else {
      return 'secondResult';
    }
 };
 var thirdFn = function(param) {
    // do something with param
    return 'thirdResult';
 };
 var errorFn = function(message) {
   // handle error
 };
 var deferred = $q.defer();
 var promise = deferred.promise.then(firstFn).then(secondFn).then(thirdFn, errorFn);

Zoals je in dit voorbeeld kunt zien, heeft elke handler in de keten de mogelijkheid om verkeer om te leiden naar de volgende fout-handler in plaats van de volgende succes-handler. In de meeste gevallen kunt u een enkele foutafhandelaar aan het einde van de keten hebben, maar u kunt ook tussenliggende foutafhandelaars hebben die proberen te herstellen.

Om snel terug te komen op uw voorbeelden (en uw vragen), wil ik alleen zeggen dat ze twee verschillende manieren vertegenwoordigen om de callback-georiënteerde API van Facebook aan te passen aan de manier waarop Angular modelwijzigingen observeert. Het eerste voorbeeld verpakt de API-aanroep in een belofte, die kan worden toegevoegd aan een scope en wordt begrepen door het sjabloonsysteem van Angular. De tweede is de meer brute-force benadering door het callback-resultaat rechtstreeks op de scope in te stellen en vervolgens $scope.$digest()aan te roepen om Angular op de hoogte te stellen van de wijziging van een externe bron.

De twee voorbeelden zijn niet direct vergelijkbaar, omdat bij de eerste de inlogstap ontbreekt. Het is echter over het algemeen wenselijk om interacties met externe API’s zoals deze in afzonderlijke services in te kapselen en de resultaten als beloften aan controllers te leveren. Op die manier kunt u uw controllers gescheiden houden van externe problemen en ze gemakkelijker testen met nepservices.


Antwoord 2, autoriteit 2%

Ik verwachtte een complex antwoord dat beide omvat: waarom ze worden gebruikt in?
algemeen en hoe het te gebruiken in Angular

Dit is de plunk voor hoekige beloften MVP(minimale levensvatbare belofte): http://plnkr.co/edit/QBAB0usWXc96TnxqKhuA?p=preview

Bron:

(voor degenen die te lui zijn om op de links te klikken)

index.html

 <head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.js"></script>
    <script src="app.js"></script>
  </head>
  <body ng-app="myModule" ng-controller="HelloCtrl">
    <h1>Messages</h1>
    <ul>
      <li ng-repeat="message in messages">{{ message }}</li>
    </ul>
  </body>
</html>

app.js

angular.module('myModule', [])
  .factory('HelloWorld', function($q, $timeout) {
    var getMessages = function() {
      var deferred = $q.defer();
      $timeout(function() {
        deferred.resolve(['Hello', 'world']);
      }, 2000);
      return deferred.promise;
    };
    return {
      getMessages: getMessages
    };
  })
  .controller('HelloCtrl', function($scope, HelloWorld) {
    $scope.messages = HelloWorld.getMessages();
  });

(Ik weet dat het je specifieke Facebook-voorbeeld niet oplost, maar ik vind de volgende fragmenten nuttig)

Via: http://markdalgleish.com/2013/06 /using-promises-in-angularjs-views/


Update 28 februari 2014:Vanaf 1.2.0 worden beloften niet langer opgelost door sjablonen.
http://www.benlesh.com/2013/02 /angularjs-creating-service-with-http.html

(plunkervoorbeeld gebruikt 1.1.5.)


Antwoord 3

Een uitgesteld vertegenwoordigt het resultaat van een asynchrone bewerking. Het onthult een interface die kan worden gebruikt voor het signaleren van de status en het resultaat van de operatie die het vertegenwoordigt. Het biedt ook een manier om de bijbehorende belofte-instantie te krijgen.

Een belofte biedt een interface voor interactie met de bijbehorende uitgestelde operatie, en geeft geïnteresseerde partijen dus toegang tot de staat en het resultaat van de uitgestelde operatie.

Bij het maken van een uitgesteld, is de status in behandeling en heeft het geen resultaat. Wanneer we het uitgestelde oplossen() of afwijzen(), verandert de status in opgelost of afgewezen. Toch kunnen we de bijbehorende belofte onmiddellijk na het maken van een uitgestelde ontvangen en zelfs interacties toewijzen aan het toekomstige resultaat. Die interacties zullen alleen plaatsvinden nadat de uitgestelde afgewezen of opgelost is.


Antwoord 4

gebruik belofte binnen een verwerkingsverantwoordelijke en zorg ervoor dat de gegevens beschikbaar zijn of niet

var app = angular.module("app",[]);
      app.controller("test",function($scope,$q){
        var deferred = $q.defer();
        deferred.resolve("Hi");
        deferred.promise.then(function(data){
        console.log(data);    
        })
      });
      angular.bootstrap(document,["app"]);
<!DOCTYPE html>
<html>
  <head>
    <script data-require="angular.js@*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
  </head>
  <body>
    <h1>Hello Angular</h1>
    <div ng-controller="test">
    </div>
  </body>
</html>

Other episodes