Terkadang saya perlu menggunakan $scope.$apply
kode saya dan terkadang muncul error "intisari sudah dalam proses". Jadi saya mulai menemukan jalan keluarnya dan menemukan pertanyaan ini: AngularJS: Mencegah error $ digest sedang berlangsung saat memanggil $ scope. $ Apply () . Namun di komentar (dan di wiki sudut) Anda dapat membaca:
Jangan lakukan jika (! $ Scope. $$ phase) $ scope. $ Apply (), artinya $ scope Anda. $ Apply () tidak cukup tinggi dalam tumpukan panggilan.
Jadi sekarang saya punya dua pertanyaan:
- Mengapa tepatnya ini merupakan anti-pola?
- Bagaimana cara menggunakan $ scope. $ Apply dengan aman?
"Solusi" lain untuk mencegah kesalahan "intisari sedang dalam proses" tampaknya menggunakan $ timeout:
$timeout(function() {
//...
});
Apakah itu cara untuk pergi? Apakah ini lebih aman? Jadi, inilah pertanyaan sebenarnya: Bagaimana saya bisa sepenuhnya menghilangkan kemungkinan kesalahan "intisari sudah dalam proses"?
PS: Saya hanya menggunakan $ scope. $ Apply in non-angularjs callbacks yang tidak sinkron. (sejauh yang saya tahu itu adalah situasi di mana Anda harus menggunakan $ scope. $ apply jika Anda ingin perubahan Anda diterapkan)
sumber
scope
dari dalam sudut atau dari luar sudut. Jadi menurut Anda ini selalu tahu, apakah Anda perlu meneleponscope.$apply
atau tidak. Dan jika Anda menggunakan kode yang sama untuk keduascope
manipulasi sudut / non-sudut , Anda salah melakukannya, itu harus selalu dipisahkan ... jadi pada dasarnya jika Anda mengalami kasus di mana Anda perlu memeriksascope.$$phase
, kode Anda tidak dirancang dengan cara yang benar, dan selalu ada cara untuk melakukannya dengan 'cara yang benar'digest already in progress
Jawaban:
Setelah menggali lebih dalam, saya dapat menjawab pertanyaan apakah selalu aman untuk digunakan
$scope.$apply
. Jawaban singkatnya adalah ya.Jawaban panjang:
Karena cara browser Anda menjalankan Javascript, tidak mungkin dua panggilan intisari bertabrakan secara kebetulan .
Karenanya kesalahan "intisari sudah dalam proses" hanya dapat terjadi dalam satu situasi: Ketika $ apply dikeluarkan di dalam $ apply lain, misalnya:
Situasi ini tidak bisa muncul jika kita menggunakan $ scope.apply dalam callback non-angularjs murni, seperti misalnya callback
setTimeout
. Jadi kode berikut ini 100% antipeluru dan tidak perlu melakukan aif (!$scope.$$phase) $scope.$apply()
bahkan yang ini aman:
Apa yang TIDAK aman (karena $ timeout - seperti semua pembantu angularjs - sudah memanggil
$scope.$apply
Anda):Ini juga menjelaskan mengapa penggunaan
if (!$scope.$$phase) $scope.$apply()
anti-pola. Anda tidak membutuhkannya jika Anda menggunakan$scope.$apply
dengan cara yang benar: Dalam callback js murni sepertisetTimeout
misalnya.Baca http://jimhoskins.com/2012/12/17/angularjs-and-apply.html untuk penjelasan lebih rinci.
sumber
$document.bind('keydown', function(e) { $rootScope.$apply(function() { // a passed through function from the controller gets executed here }); });
saya benar-benar tidak tahu mengapa saya harus membuat $ apply di sini, karena saya menggunakan $ document.bind ..function $DocumentProvider(){ this.$get = ['$window', function(window){ return jqLite(window.document); }]; }
Tidak ada aplikasi di sana.$timeout
secara semantik berarti menjalankan kode setelah penundaan. Ini mungkin hal yang secara fungsional aman untuk dilakukan tetapi ini adalah peretasan. Seharusnya ada cara yang aman untuk menggunakan $ apply ketika Anda tidak dapat mengetahui apakah suatu$digest
siklus sedang berlangsung atau Anda sudah berada di dalam$apply
.Ini jelas merupakan anti-pola sekarang. Saya telah melihat intisari meledak bahkan jika Anda memeriksa fase $$. Anda hanya tidak seharusnya mengakses API internal yang dilambangkan dengan
$$
prefiks.Kamu harus menggunakan
karena ini adalah metode yang disukai di Angular ^ 1.4 dan secara khusus diekspos sebagai API untuk lapisan aplikasi.
sumber
Dalam kasus apa pun saat intisari Anda sedang berlangsung dan Anda mendorong layanan lain untuk mencerna, itu hanya memberikan kesalahan yaitu intisari sudah dalam proses. jadi untuk menyembuhkan ini, Anda memiliki dua pilihan. Anda dapat memeriksa intisari lain yang sedang berlangsung seperti polling.
Pertama
jika kondisi di atas benar, maka Anda dapat menerapkan $ scope Anda. $ apply otherwies not and
solusi kedua adalah menggunakan $ timeout
ia tidak akan membiarkan intisari lainnya mulai sampai $ timeout menyelesaikan eksekusinya.
sumber
$scope.$apply();
.$timeout
adalah kuncinya! itu berhasil dan kemudian saya menemukan bahwa itu direkomendasikan juga.scope.$apply
memicu$digest
siklus yang fundamental untuk pengikatan data 2 arahSebuah
$digest
siklus memeriksa objek yaitu model (tepatnya$watch
) yang dilampirkan untuk$scope
menilai apakah nilainya telah berubah dan jika mendeteksi perubahan maka diperlukan langkah-langkah yang diperlukan untuk memperbarui tampilan.Sekarang ketika Anda menggunakan
$scope.$apply
Anda menghadapi kesalahan "Sudah dalam proses" sehingga cukup jelas bahwa $ digest sedang berjalan tetapi apa yang memicunya?ans -> setiap
$http
panggilan, semua ng-klik, ulangi, tampilkan, sembunyikan dll memicu$digest
siklus DAN BAGIAN TERBURUK JALAN SETIAP $ LINGKUP.misalnya, laman Anda memiliki 4 pengontrol atau arahan A, B, C, D
Jika Anda memiliki 4
$scope
properti di masing-masing properti, maka Anda memiliki total 16 $ properti cakupan di halaman Anda.Jika Anda memicu
$scope.$apply
di pengontrol D maka a$digest
siklus akan memeriksa semua 16 nilai !!! ditambah semua properti $ rootScope.Jawaban -> tetapi
$scope.$digest
memicu$digest
on child dan cakupan yang sama sehingga hanya akan memeriksa 4 properti. Jadi jika Anda yakin bahwa perubahan D tidak akan mempengaruhi A, B, C maka gunakan$scope.$diges
t tidak$scope.$apply
.Jadi, sekadar ng-click atau ng-show / hide dapat memicu
$digest
siklus pada lebih dari 100 properti bahkan ketika pengguna belum mengaktifkan peristiwa apa pun !sumber
Gunakan
$timeout
, itu cara yang disarankan.Skenario saya adalah saya perlu mengubah item pada halaman berdasarkan data yang saya terima dari WebSocket. Dan karena berada di luar Angular, tanpa batas waktu $, satu-satunya model yang akan diubah tetapi tampilan tidak akan berubah. Karena Angular tidak tahu bahwa bagian data telah diubah.
$timeout
pada dasarnya memberi tahu Angular untuk membuat perubahan di babak $ digest berikutnya.Saya mencoba yang berikut ini juga dan berhasil. Perbedaannya bagi saya adalah $ batas waktu lebih jelas.
sumber
$http
). Jika tidak, Anda harus mengulangi kode ini di semua tempat.$scope.$apply
jika Anda menggunakansetTimeout
atau$timeout
Saya menemukan solusi yang sangat keren:
menyuntikkan itu di mana Anda membutuhkan:
sumber