wake-up-neo.com

AngularJS: Wo kann man Versprechen verwenden?

Ich habe einige Beispiele von Facebook-Anmeldediensten gesehen, die versprochen verwendeten, um auf die FB Graph-API zuzugreifen.

Beispiel 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;
}

Und Dienste, die "$scope.$digest() // Manual scope evaluation" verwendet haben, als sie die Antwort erhalten haben

Beispiel 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

Die Fragen sind:

  • Was ist der nterschied in den obigen Beispielen?
  • Was sind die Gründe und Fälle, um $ q Service zu verwenden?
  • Und wie funktioniert es Arbeit?
141
Maksym

Dies wird keine vollständige Antwort auf Ihre Frage sein, aber dies wird Ihnen und anderen hoffentlich helfen, wenn Sie versuchen, die Dokumentation zum Dienst $q Zu lesen. Ich brauchte eine Weile, um es zu verstehen.

Lassen Sie uns AngularJS für einen Moment beiseite legen und nur die Facebook-API-Aufrufe betrachten. Beide API-Aufrufe verwenden einen Rückruf Mechanismus, um den Anrufer zu benachrichtigen, wenn die Antwort von Facebook verfügbar ist:

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

Dies ist ein Standardmuster für die Behandlung asynchroner Vorgänge in JavaScript und anderen Sprachen.

Ein großes Problem mit diesem Muster tritt auf, wenn Sie eine Folge von asynchronen Operationen ausführen müssen, wobei jede nachfolgende Operation vom Ergebnis der vorherigen Operation abhängt. Das macht dieser Code:

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

Zuerst wird versucht, sich anzumelden, und erst nachdem überprüft wurde, ob die Anmeldung erfolgreich war, wird die Anforderung an die Graph-API gesendet.

Sogar in diesem Fall, in dem nur zwei Vorgänge verkettet werden, wird es unordentlich. Die Methode askFacebookForAuthentication akzeptiert einen Rückruf für Fehler und Erfolg, aber was passiert, wenn FB.login Erfolgreich ist, aber FB.api Fehlschlägt? Diese Methode ruft immer den Callback success auf, unabhängig vom Ergebnis der Methode FB.api.

Stellen Sie sich nun vor, Sie versuchen, eine robuste Sequenz von drei oder mehr asynchronen Vorgängen so zu codieren, dass Fehler bei jedem Schritt richtig behandelt werden und nach einigen Wochen für alle anderen oder sogar für Sie lesbar sind. Möglich, aber es ist sehr einfach, diese Rückrufe einfach weiter zu verschachteln und dabei den Überblick über Fehler zu verlieren.

Lassen Sie uns nun die Facebook-API für einen Moment beiseite legen und betrachten wir nur die Angular Promises API, wie sie vom Dienst $q Implementiert wurde. Das von diesem Dienst implementierte Muster ist ein Versuch dazu Verwandeln Sie asynchrone Programmierung wieder in etwas, das einer linearen Folge einfacher Anweisungen ähnelt, mit der Möglichkeit, einen Fehler bei jedem Schritt des Weges zu "werfen" und ihn am Ende zu verarbeiten, semantisch ähnlich dem bekannten try/catch - Block.

Betrachten Sie dieses erfundene Beispiel. Angenommen, wir haben zwei Funktionen, bei denen die zweite Funktion das Ergebnis der ersten aufnimmt:

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

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

 secondFn(firstFn()); 

Stellen Sie sich nun vor, dass firstFn und secondFn beide viel Zeit in Anspruch nehmen. Daher möchten wir diese Sequenz asynchron verarbeiten. Zuerst erstellen wir ein neues deferred -Objekt, das eine Kette von Operationen darstellt:

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

Die Eigenschaft promise repräsentiert das endgültige Ergebnis der Kette. Wenn Sie ein Versprechen sofort nach dem Erstellen protokollieren, sehen Sie, dass es sich nur um ein leeres Objekt handelt ({}). Noch nichts zu sehen, mach weiter.

Bisher ist unser Versprechen nur der Ausgangspunkt in der Kette. Fügen wir nun unsere beiden Operationen hinzu:

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

Die then -Methode fügt der Kette einen Schritt hinzu und gibt dann ein neues Versprechen zurück, das das endgültige Ergebnis der erweiterten Kette darstellt. Sie können beliebig viele Schritte hinzufügen.

Bisher haben wir unsere Funktionskette eingerichtet, aber tatsächlich ist nichts passiert. Zum Starten rufen Sie deferred.resolve Auf und geben den Anfangswert an, den Sie an den ersten tatsächlichen Schritt in der Kette übergeben möchten:

 deferred.resolve('initial value');

Und dann ... passiert immer noch nichts. Um sicherzustellen, dass Modelländerungen ordnungsgemäß beobachtet werden, ruft Angular= erst beim nächsten Aufruf von $apply Den ersten Schritt in der Kette auf:

 deferred.resolve('initial value');
 $rootScope.$apply();

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

Was ist also mit der Fehlerbehandlung? Bisher haben wir nur einen Erfolgs-Handler für jeden Schritt in der Kette angegeben. then akzeptiert auch einen Fehlerhandler als optionales zweites Argument. Hier ist ein weiteres, längeres Beispiel für eine Versprechen-Kette, diesmal mit Fehlerbehandlung:

 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);

Wie Sie in diesem Beispiel sehen können, hat jeder Handler in der Kette die Möglichkeit, den Verkehr zum nächsten Fehler Handler anstatt zum nächsten Erfolg Handler umzuleiten. In den meisten Fällen können Sie einen einzelnen Fehlerbehandler am Ende der Kette haben, aber Sie können auch Zwischenfehlerbehandler haben, die eine Wiederherstellung versuchen.

Um schnell zu Ihren Beispielen (und Ihren Fragen) zurückzukehren, möchte ich nur sagen, dass sie zwei verschiedene Möglichkeiten darstellen, um die Callback-orientierte API von Facebook an Angulars Art der Beobachtung von Modelländerungen anzupassen. Das erste Beispiel umschließt den API-Aufruf mit einer Zusage, die einem Bereich hinzugefügt werden kann und vom Angular-Template-System verstanden wird. Der zweite Ansatz ist der brachialere: Setzen Sie das Rückrufergebnis direkt auf den Gültigkeitsbereich und rufen Sie dann $scope.$digest() auf, um Angular) auf die Änderung von einer externen Quelle aufmerksam zu machen.

Die beiden Beispiele sind nicht direkt vergleichbar, da dem ersten der Anmeldeschritt fehlt. Im Allgemeinen ist es jedoch wünschenswert, Interaktionen mit externen APIs wie diesen in separaten Services zu kapseln und die Ergebnisse als Versprechen an die Controller zu liefern. Auf diese Weise können Sie Ihre Controller von externen Anliegen trennen und sie mit Hilfe von Mock-Services einfacher testen.

401
karlgold

Ich habe eine komplexe Antwort erwartet, die beide Aspekte abdeckt: Warum werden sie im Allgemeinen verwendet und wie wird sie in Angular verwendet?

Dies ist das Plunk für Winkelversprechen MVP (Mindestversprechen): http: // plnkr .co/edit/QBAB0usWXc96TnxqKhuA? p = preview

Quelle:

(für diejenigen, die zu faul sind, um auf die Links zu klicken)

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();

  });

(Ich weiß, dass es Ihr spezifisches Facebook-Beispiel nicht löst, aber ich finde folgende Ausschnitte nützlich)

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


Update 28. Februar 2014: Versprechen werden ab 1.2.0 nicht mehr durch Templates gelöst.http://www.benlesh.com/2013/02/angularjs-creating-service-with-http.html

(Plunker-Beispiel verwendet 1.1.5.)

9
Mars Robertson

Ein Aufgeschobener ist das Ergebnis einer asynchronen Operation. Es stellt eine Schnittstelle zur Verfügung, über die der Status und das Ergebnis der von ihm dargestellten Operation signalisiert werden können. Es bietet auch eine Möglichkeit, die zugehörige Versprechungsinstanz abzurufen.

Ein Versprechen bietet eine Schnittstelle für die Interaktion mit der damit verbundenen Zurückstellung und ermöglicht Interessenten so den Zugriff auf den Status und das Ergebnis der zurückgestellten Operation.

Beim Erstellen eines verzögerten Auftrags steht dessen Status aus und es hat kein Ergebnis. Wenn wir den Aufschub auflösen () oder ablehnen (), ändert sich der Status in "Aufgelöst" oder "Abgelehnt". Dennoch können wir das zugehörige Versprechen sofort nach dem Erstellen eines verzögerten Versprechens erhalten und sogar Interaktionen mit dem zukünftigen Ergebnis des Versprechens zuweisen. Diese Wechselwirkungen treten erst auf, nachdem die Zurückstellung abgelehnt oder gelöst wurde.

1
Ram G

verwenden Sie Versprechen innerhalb eines Controllers und stellen Sie sicher, dass die Daten verfügbar sind oder nicht

 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="[email protected]*" 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>
1
Manivannan A