Widziałem kilka przykładów usług logowania na Facebooku, które korzystały z obietnic dostępu do FB Graph API.
Przykład 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;
}
I usługi, które były używane, "$scope.$digest() // Manual scope evaluation"
gdy otrzymałem odpowiedź
Przykład 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
});
}
}
Oto pytania:
- Jaka jest różnica w powyższych przykładach?
- Jakie są powody i przypadki korzystania z usługi $ q ?
- A jak to działa ?
Odpowiedzi:
To nie będzie pełna odpowiedź na twoje pytanie, ale miejmy nadzieję, że pomoże to tobie i innym podczas próby zapoznania się z dokumentacją dotyczącą
$q
usługi. Zajęło mi trochę czasu, zanim to zrozumiałem.Odłóżmy na chwilę AngularJS i rozważmy wywołania API Facebooka. Oba wywołania API wykorzystują mechanizm wywołania zwrotnego , aby powiadomić dzwoniącego o dostępności odpowiedzi z Facebooka:
Jest to standardowy wzorzec do obsługi operacji asynchronicznych w JavaScript i innych językach.
Jeden duży problem z tym wzorcem pojawia się, gdy trzeba wykonać sekwencję operacji asynchronicznych, w których każda kolejna operacja zależy od wyniku poprzedniej operacji. To właśnie robi ten kod:
Najpierw próbuje się zalogować, a dopiero po sprawdzeniu, czy logowanie się powiodło, wysyła żądanie do Graph API.
Nawet w tym przypadku, który polega tylko na połączeniu dwóch operacji w łańcuch, sytuacja zaczyna się komplikować. Metoda
askFacebookForAuthentication
akceptuje wywołanie zwrotne w przypadku niepowodzenia i sukcesu, ale co się dzieje, gdy sięFB.login
powiedzie, aleFB.api
zawiedzie? Ta metoda zawsze wywołujesuccess
wywołanie zwrotne niezależnie od wynikuFB.api
metody.Teraz wyobraź sobie, że próbujesz zakodować solidną sekwencję trzech lub więcej operacji asynchronicznych w sposób, który poprawnie obsługuje błędy na każdym kroku i będzie czytelny dla każdego, a nawet dla Ciebie po kilku tygodniach. Możliwe, ale bardzo łatwo jest po prostu zagnieżdżać te wywołania zwrotne i zgubić błędy po drodze.
Teraz odłóżmy na chwilę na bok API Facebooka i po prostu rozważmy API Angular Promises zaimplementowane przez
$q
usługę. Wzorzec zaimplementowany przez tę usługę jest próbą przekształcenia programowania asynchronicznego z powrotem w coś przypominającego liniową serię prostych instrukcji, z możliwością `` rzucenia '' błędu na dowolnym etapie i obsłużenia go na końcu, semantycznie podobnym do znajomytry/catch
blok.Rozważmy ten wymyślony przykład. Powiedzmy, że mamy dwie funkcje, gdzie druga funkcja zużywa wynik pierwszej:
Teraz wyobraź sobie, że pierwszeFn i secondFn zajmują dużo czasu, więc chcemy przetwarzać tę sekwencję asynchronicznie. Najpierw tworzymy nowy
deferred
obiekt, który reprezentuje łańcuch operacji:promise
Właściwość reprezentuje ewentualny wynik łańcuchu. Jeśli zarejestrujesz obietnicę natychmiast po jej utworzeniu, zobaczysz, że jest to po prostu pusty obiekt ({}
). Nie ma jeszcze nic do oglądania, przejdź od razu.Jak dotąd nasza obietnica reprezentuje jedynie punkt wyjścia w łańcuchu. Teraz dodajmy nasze dwie operacje:
then
Metoda dodaje krok do łańcucha, a następnie zwraca nową obietnicę reprezentujący ewentualny wynik rozszerzonego łańcucha. Możesz dodać tyle kroków, ile chcesz.Do tej pory skonfigurowaliśmy nasz łańcuch funkcji, ale tak naprawdę nic się nie wydarzyło. Rozpoczynasz od wywołania
deferred.resolve
i określenia wartości początkowej, którą chcesz przekazać do pierwszego rzeczywistego kroku w łańcuchu:A potem ... nadal nic się nie dzieje. Aby upewnić się, że zmiany modelu są prawidłowo obserwowane, Angular nie wywołuje pierwszego kroku w łańcuchu, dopóki nie
$apply
zostanie wywołany następny raz :A co z obsługą błędów? Do tej pory określiliśmy tylko procedurę obsługi sukcesu na każdym etapie łańcucha.
then
akceptuje również procedurę obsługi błędów jako opcjonalny drugi argument. Oto kolejny, dłuższy przykład łańcucha obietnic, tym razem z obsługą błędów:Jak widać w tym przykładzie, każdy program obsługi w łańcuchu ma możliwość skierowania ruchu do następnego programu obsługi błędów zamiast do następnego programu obsługi sukcesu . W większości przypadków na końcu łańcucha może znajdować się jedna procedura obsługi błędów, ale można również mieć pośrednie procedury obsługi błędów, które próbują odzyskać.
Aby szybko wrócić do twoich przykładów (i twoich pytań), powiem tylko, że reprezentują one dwa różne sposoby dostosowania API zorientowanego na wywołania zwrotne Facebooka do sposobu obserwowania zmian modelu przez Angular. Pierwszy przykład opakowuje wywołanie API w obietnicę, którą można dodać do zakresu i która jest rozumiana przez system szablonów Angulara. Drugi przyjmuje bardziej brutalne podejście polegające na ustawianiu wyniku wywołania zwrotnego bezpośrednio w zakresie, a następnie wywoływaniu,
$scope.$digest()
aby Angular był świadomy zmiany z zewnętrznego źródła.Te dwa przykłady nie są bezpośrednio porównywalne, ponieważ w pierwszym brakuje etapu logowania. Jednak ogólnie pożądane jest hermetyzowanie interakcji z zewnętrznymi interfejsami API, takimi jak ten, w oddzielnych usługach i dostarczanie wyników kontrolerom zgodnie z obietnicą. W ten sposób możesz oddzielić kontrolery od problemów zewnętrznych i łatwiej je przetestować za pomocą usług pozorowanych.
źródło
then
metod jest użycie$q.all
. Krótki poradnik na ten temat można znaleźć tutaj .$q.all
jest odpowiedni, jeśli musisz czekać na zakończenie wielu niezależnych operacji asynchronicznych. Nie zastępuje tworzenia łańcucha, jeśli każda operacja zależy od wyniku poprzedniej operacji.return 'firstResult'
część nareturn $q.resolve('firstResult')
, jaka będzie różnica?To jest podstawa dla kątowych obietnic MVP (minimalna realna obietnica) : http://plnkr.co/edit/QBAB0usWXc96TnxqKhuA?p=preview
Źródło:
(dla osób zbyt leniwych, by klikać linki)
index.html
app.js
(Wiem, że to nie rozwiązuje twojego konkretnego przykładu na Facebooku, ale uważam następujące fragmenty za przydatne)
Via: http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/
Aktualizacja 28 lutego 2014: od 1.2.0 obietnice nie są już rozwiązywane za pomocą szablonów. http://www.benlesh.com/2013/02/angularjs-creating-service-with-http.html
(przykład plunkera używa 1.1.5.)
źródło
Odroczony reprezentuje wynik operacji asynchronicznej. Udostępnia interfejs, którego można użyć do sygnalizacji stanu i wyniku operacji, którą reprezentuje. Zapewnia również sposób uzyskania powiązanej instancji obietnicy.
Obietnica zapewnia interfejs do interakcji z nią pokrewną odroczoną, a więc umożliwia zainteresowanym stronom uzyskanie dostępu do stanu i wyniku odroczonej operacji.
Podczas tworzenia odroczonego stan jest w toku i nie ma żadnego wyniku. Kiedy rozwiązujemy () lub odrzucamy () odroczony, zmienia on swój stan na rozwiązany lub odrzucony. Mimo to możemy uzyskać powiązaną obietnicę natychmiast po utworzeniu odroczonego, a nawet przypisać interakcje z jej przyszłym wynikiem. Te interakcje wystąpią dopiero po odroczonym odrzuceniu lub rozwiązaniu.
źródło
skorzystaj z obietnicy w kontrolerze i upewnij się, że dane są dostępne, czy nie
źródło