AngularJS: Vaatler nerede kullanılır?


141

FB Graph API'sına erişme vaatlerini kullanan bazı Facebook Login hizmetleri örnekleri gördüm .

Örnek 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;
}

Ve "$scope.$digest() // Manual scope evaluation"yanıt alındığında kullanılan hizmetler

Örnek 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

Sorular:

  • Yukarıdaki örneklerde ne fark var ?
  • Nedir nedenleri ve olgular kullanmak $ q hizmeti?
  • Ve nasıl çalışır ?

9
vaatlerin ne olduğunu ve neden genel olarak kullanıldıklarını okumalısınız gibi geliyorlar ... açısal için özel değiller ve çok fazla kullanılabilir malzeme var
charlietfl

1
@charlietfl, iyi bir nokta, ama ikisini de kapsayacak karmaşık bir cevap bekliyordum: neden genel olarak kullanıldıkları ve Angular'da nasıl kullanılacağı. Öneriniz için teşekkürler
Maksym

Yanıtlar:


401

Bu, sorunuza tam bir cevap olmayacaktır, ancak umarım bu, $qhizmetle ilgili belgeleri okumaya çalıştığınızda size ve diğerlerine yardımcı olacaktır . Anlamak biraz zaman aldı.

Bir an için AngularJS'yi bir kenara bırakalım ve sadece Facebook API çağrılarını düşünelim. Her iki API çağrısı da Facebook'tan yanıt geldiğinde arayanı bilgilendirmek için bir geri arama mekanizması kullanır:

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

Bu, JavaScript ve diğer dillerde eşzamansız işlemleri yapmak için standart bir modeldir.

Bu paternle ilgili büyük bir sorun, birbirini takip eden her bir işlemin önceki işlemin sonucuna bağlı olduğu bir dizi asenkron işlem gerçekleştirmeniz gerektiğinde ortaya çıkar. Bu kod bunu yapıyor:

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

İlk olarak giriş yapmaya çalışır ve daha sonra sadece girişin başarılı olduğunu doğruladıktan sonra Grafik API'sına istekte bulunur.

Sadece iki operasyonu birbirine bağlayan bu durumda bile, işler dağınık olmaya başlar. Yöntem askFacebookForAuthentication, başarısızlık ve başarı için geri aramayı kabul eder, ancak FB.loginbaşarılı olduğunda ancak FB.apibaşarısız olduğunda ne olur ? Bu yöntem her zamansuccess , FB.apiyöntemin sonucundan bağımsız olarak geri aramayı .

Şimdi, üç veya daha fazla eşzamansız işlemden oluşan sağlam bir diziyi, her adımdaki hataları düzgün bir şekilde işleyecek ve birkaç hafta sonra sizin için ve hatta sizin için okunabilecek bir şekilde kodlamaya çalıştığınızı düşünün. Mümkün, ancak bu geri çağrıları iç içe geçirmeye devam etmek ve yol boyunca hataları izlemek çok kolay.

Şimdi bir an için Facebook API'sını bir kenara bırakalım ve $qhizmet tarafından uygulanan Açısal Vaatler API'sını ele alalım . Bu hizmet tarafından uygulanan örüntü, eşzamansız programlamayı, yolun herhangi bir adımında bir hata 'atma' ve sonunda semantik olarak benzer şekilde işleme yeteneği ile, bir dizi basit ifadeye benzeyen bir şeye dönüşme girişimidir. tanıdıktry/catch blok.

Bu uydurulmuş örneği ele alalım. Diyelim ki iki fonksiyon var, burada ikinci fonksiyon birincinin sonucunu tüketiyor:

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

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

 secondFn(firstFn()); 

Şimdi firstFn ve secondFn'nin tamamlanması uzun sürdüğünü hayal edin, bu yüzden bu diziyi eşzamansız olarak işlemek istiyoruz. İlk önce bir deferredoperasyon zincirini temsil eden yeni bir nesne yaratırız :

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

promiseÖzelliği zincirinin nihai sonucunu temsil eder. Bir vaadi oluşturduktan hemen sonra kaydederseniz, bunun yalnızca boş bir nesne ( {}) olduğunu görürsünüz . Henüz görecek bir şey yok, sağa doğru hareket et.

Şu ana kadar sözümüz sadece zincirdeki başlangıç ​​noktasını temsil ediyor. Şimdi iki operasyonumuzu ekleyelim:

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

thenYöntem zincirine adım ekler ve sonra uzatılmış zincir nihai sonucunu temsil eden yeni bir söz döndürür. İstediğiniz kadar adım ekleyebilirsiniz.

Şimdiye kadar, fonksiyon zincirimizi kurduk, ama aslında hiçbir şey olmadı. deferred.resolveZincirdeki ilk gerçek adıma geçmek istediğiniz başlangıç ​​değerini belirterek, çağrı yaparak işe başlayabilirsiniz:

 deferred.resolve('initial value');

Ve sonra ... hala hiçbir şey olmuyor. Model değişikliklerinin düzgün bir şekilde gözlemlendiğinden emin olmak için, Angular aslında bir sonraki sefer çağrılıncaya kadar zincirdeki ilk adımı çağırmaz $apply:

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

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

Hata işleme ne olacak? Şimdiye kadar sadece zincirdeki her adımda bir başarı işleyicisi belirledik . thenhata işleyicisini isteğe bağlı ikinci bir bağımsız değişken olarak da kabul eder. İşte bu sefer bir hata zincirinin daha uzun bir örneği, bu sefer hata işleme ile:

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

Bu örnekte görebileceğiniz gibi, zincirdeki her işleyici trafiği bir sonraki başarı işleyicisi yerine bir sonraki hata işleyicisine yönlendirme olanağına sahiptir . Çoğu durumda zincirin sonunda tek bir hata işleyiciniz olabilir, ancak kurtarmaya çalışan ara hata işleyicileriniz de olabilir.

Hızlı bir şekilde örneklerinize (ve sorularınıza) geri dönmek için, Facebook'un geri arama odaklı API'sını Angular'ın model değişikliklerini gözlemleme yöntemine uyarlamanın iki farklı yolunu temsil ettiklerini söyleyeceğim. İlk örnek, API çağrısını bir kapsama eklenebilen ve Angular'ın şablonlama sistemi tarafından anlaşılan bir söz ile tamamlar. İkincisi, geri arama sonucunu doğrudan kapsam üzerine ayarlamak ve ardından $scope.$digest()Angular'ı harici bir kaynaktan gelen değişiklikten haberdar etmek için daha kaba-kuvvet yaklaşımı alır .

İki örnek doğrudan karşılaştırılamaz, çünkü ilki giriş adımını kaçırır. Bununla birlikte, genellikle bu gibi harici API'lerle etkileşimleri ayrı hizmetlerde kapsüllemek ve sonuçları denetleyicilere söz olarak vermek arzu edilir. Bu şekilde denetleyicilerinizi dış endişelerden ayrı tutabilir ve sahte hizmetler ile daha kolay test edebilirsiniz.


5
Bence harika bir cevap! Benim için en önemli şey, vaat gerçekten olduğunda genel durumu tanımlamaktı. Dürüst olmak gerekirse, başka bir gerçek örnek umuyordum (Facebook'ta olduğu gibi), ama bu da sanırım çalışıyor. Çok teşekkürler!
Maksym

2
Birden çok thenyöntemi zincirlemenin bir alternatifi kullanmaktır $q.all. Bununla ilgili hızlı bir eğitim burada bulunabilir .
Bogdan

2
$q.allbirden fazla bağımsız eşzamansız işlemin tamamlanmasını beklemeniz gerekiyorsa uygundur . Her işlem bir önceki işlemin sonucuna bağlıysa zincirlemenin yerini almaz.
karlgold

1
o zamanki zincirleme burada kısaca açıklanır. Tam potansiyeline kadar anlamama ve kullanmama yardımcı oldu. Teşekkürler
Tushar Joshi

1
Harika cevap @karlgold! Bir sorum var. Son kod snippet'inde return 'firstResult'parçayı olarak değiştirirseniz return $q.resolve('firstResult'), fark ne olur?
teknopil

9

Her ikisini de kapsayacak karmaşık bir cevap bekledim: neden genel olarak kullanıldıkları ve Angular'da nasıl kullanıldıkları

Bu açısal vaatler MVP (minimum uygulanabilir vaat) için bir cesaret : http://plnkr.co/edit/QBAB0usWXc96TnxqKhuA?p=preview

Kaynak:

(bağlantıları tıklamayacak kadar tembel olanlar için)

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

  });

(Belirli Facebook örneğinizi çözmediğini biliyorum ancak aşağıdaki snippet'leri yararlı buluyorum)

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


28 Şubat 2014 Güncellemesi: 1.2.0 itibariyle vaatler artık şablonlar tarafından çözülmüyor. http://www.benlesh.com/2013/02/angularjs-creating-service-with-http.html

(dalgıç örneği 1.1.5 kullanır.)


afaik çok seviyoruz çünkü
tembeliz

Bu bana $ q, ertelenmiş ve zincirleme anlamamda yardımcı oldu. sonra işlev çağrıları, çok teşekkürler
aliopi

1

Ertelenmiş bir asenkronik operasyonun sonucunu temsil eder. Durumu ve temsil ettiği işlemin sonucunu bildirmek için kullanılabilecek bir arabirim ortaya koyar. Ayrıca ilişkili söz örneğini almanın bir yolunu sağlar.

Bir söz, ilgili ertelenmişlerle etkileşime girmek için bir arayüz sağlar ve böylece ilgili tarafların devlete ve ertelenmiş operasyonun sonucuna erişmesine izin verir.

Ertelenirken, durumu beklemededir ve herhangi bir sonucu yoktur. Ertelenmeyi çözdüğümüzde () veya reddettiğimizde (), durumunu çözülecek veya reddedilecek şekilde değiştirir. Yine de, ertelenmiş bir oluşturma oluşturduktan hemen sonra ilişkili sözü alabilir ve hatta gelecekteki sonucu ile etkileşimler atayabiliriz. Bu etkileşimler sadece ertelenen veya reddedildikten sonra gerçekleşecektir.


1

bir denetleyici içinde vaat kullanın ve verilerin mevcut olup olmadığından emin olun

 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>

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.