Ini tidak akan menjadi jawaban lengkap untuk pertanyaan Anda, tetapi mudah-mudahan ini akan membantu Anda dan orang lain ketika Anda mencoba membaca dokumentasi pada $q
layanan. Butuh beberapa saat untuk memahaminya.
Mari kita singkirkan AngularJS sejenak dan pertimbangkan saja panggilan Facebook API. Kedua panggilan API menggunakan mekanisme panggilan balik untuk memberi tahu penelepon ketika respons dari Facebook tersedia:
facebook.FB.api('/' + item, function (result) {
if (result.error) {
// handle error
} else {
// handle success
}
});
// program continues while request is pending
...
Ini adalah pola standar untuk menangani operasi asinkron dalam JavaScript dan bahasa lainnya.
Satu masalah besar dengan pola ini muncul ketika Anda perlu melakukan urutan operasi asinkron, di mana setiap operasi berturut-turut tergantung pada hasil operasi sebelumnya. Itulah yang dilakukan kode ini:
FB.login(function(response) {
if (response.authResponse) {
FB.api('/me', success);
} else {
fail('User cancelled login or did not fully authorize.');
}
});
Pertama mencoba masuk, dan kemudian hanya setelah memverifikasi bahwa login berhasil apakah itu membuat permintaan ke API Grafik.
Bahkan dalam kasus ini, yang hanya menggabungkan dua operasi, semuanya mulai menjadi berantakan. Metode askFacebookForAuthentication
menerima panggilan balik untuk kegagalan dan kesuksesan, tetapi apa yang terjadi ketika FB.login
berhasil tetapi FB.api
gagal? Metode ini selalu memanggil success
balik tanpa memperhatikan hasil dari FB.api
metode.
Sekarang bayangkan Anda mencoba untuk mengode urutan kuat dari tiga atau lebih operasi asinkron, dengan cara yang benar menangani kesalahan pada setiap langkah dan akan dapat dibaca oleh orang lain atau bahkan untuk Anda setelah beberapa minggu. Mungkin, tapi sangat mudah untuk tetap bersarang panggilan balik itu dan kehilangan jejak kesalahan di sepanjang jalan.
Sekarang, mari kita singkirkan API Facebook sejenak dan pertimbangkan saja Angular Promises API, seperti yang diterapkan oleh $q
layanan. Pola yang diterapkan oleh layanan ini adalah upaya untuk mengubah pemrograman asinkron menjadi sesuatu yang menyerupai serangkaian pernyataan linier sederhana, dengan kemampuan untuk 'melempar' kesalahan pada langkah apa pun dan menanganinya di akhir, secara semantik mirip dengan try/catch
blok akrab .
Pertimbangkan contoh yang dibuat-buat ini. Katakanlah kita memiliki dua fungsi, di mana fungsi kedua mengkonsumsi hasil dari yang pertama:
var firstFn = function(param) {
// do something with param
return 'firstResult';
};
var secondFn = function(param) {
// do something with param
return 'secondResult';
};
secondFn(firstFn());
Sekarang bayangkan bahwa firstFn dan secondFn membutuhkan waktu lama untuk diselesaikan, jadi kami ingin memproses urutan ini secara tidak sinkron. Pertama, kita membuat deferred
objek baru , yang mewakili rantai operasi:
var deferred = $q.defer();
var promise = deferred.promise;
The promise
properti merupakan hasil akhir dari rantai. Jika Anda mencatat janji segera setelah membuatnya, Anda akan melihat bahwa itu hanyalah objek kosong ( {}
). Belum ada yang bisa dilihat, terus bergerak.
Sejauh ini janji kami hanya mewakili titik awal dalam rantai. Sekarang mari kita tambahkan dua operasi kami:
promise = promise.then(firstFn).then(secondFn);
The then
Metode menambahkan langkah untuk rantai dan kemudian kembali janji baru yang mewakili hasil akhir dari rantai diperpanjang. Anda dapat menambahkan langkah sebanyak yang Anda mau.
Sejauh ini, kami telah mengatur rantai fungsi kami, tetapi tidak ada yang benar-benar terjadi. Anda memulai sesuatu dengan menelepon deferred.resolve
, menentukan nilai awal yang ingin Anda sampaikan ke langkah aktual pertama dalam rantai:
deferred.resolve('initial value');
Dan kemudian ... masih tidak ada yang terjadi. Untuk memastikan bahwa perubahan model diamati dengan benar, Angular tidak benar-benar memanggil langkah pertama dalam rantai sampai waktu berikutnya $apply
disebut:
deferred.resolve('initial value');
$rootScope.$apply();
// or
$rootScope.$apply(function() {
deferred.resolve('initial value');
});
Jadi bagaimana dengan penanganan kesalahan? Sejauh ini kami hanya menetapkan penangan sukses di setiap langkah dalam rantai. then
juga menerima penangan kesalahan sebagai argumen kedua opsional. Inilah contoh lain dari rantai janji yang lebih panjang, kali ini dengan penanganan kesalahan:
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);
Seperti yang dapat Anda lihat dalam contoh ini, setiap penangan dalam rantai memiliki kesempatan untuk mengalihkan lalu lintas ke penangan kesalahan berikutnya alih-alih penangan keberhasilan berikutnya . Dalam kebanyakan kasus, Anda dapat memiliki penangan kesalahan tunggal di akhir rantai, tetapi Anda juga dapat memiliki penangan kesalahan menengah yang mencoba pemulihan.
Untuk segera kembali ke contoh Anda (dan pertanyaan Anda), saya hanya akan mengatakan bahwa mereka mewakili dua cara berbeda untuk mengadaptasi API berorientasi panggilan balik Facebook dengan cara Angular mengamati perubahan model. Contoh pertama membungkus panggilan API dalam sebuah janji, yang dapat ditambahkan ke ruang lingkup dan dipahami oleh sistem templating Angular. Yang kedua mengambil pendekatan yang lebih kasar dari pengaturan hasil panggilan balik langsung pada ruang lingkup, dan kemudian panggilan $scope.$digest()
untuk membuat Angular menyadari perubahan dari sumber eksternal.
Kedua contoh tidak dapat dibandingkan secara langsung, karena yang pertama tidak memiliki langkah login. Namun, umumnya diinginkan untuk merangkum interaksi dengan API eksternal seperti ini di layanan terpisah, dan memberikan hasilnya kepada pengontrol sesuai janji. Dengan begitu Anda dapat memisahkan pengontrol Anda dari masalah eksternal, dan mengujinya dengan lebih mudah dengan layanan tiruan.