Bu, sorunuza tam bir cevap olmayacaktır, ancak umarım bu, $q
hizmetle 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.login
başarılı olduğunda ancak FB.api
başarısız olduğunda ne olur ? Bu yöntem her zamansuccess
, FB.api
yö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 $q
hizmet 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 deferred
operasyon 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);
then
Yö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.resolve
Zincirdeki 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 . then
hata 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.