Pembaruan 10/9/2013: Lihat visualisasi interaktif run loop ini: https://machty.s3.amazonaws.com/ember-run-loop-visual/index.html
Pembaruan 5/9/2013: semua konsep dasar di bawah ini masih mutakhir, tetapi sejak komit ini , implementasi Ember Run Loop telah dipisahkan menjadi pustaka terpisah yang disebut backburner.js , dengan beberapa perbedaan API yang sangat kecil.
Pertama, baca ini:
http://blog.sproutcore.com/the-run-loop-part-1/
http://blog.sproutcore.com/the-run-loop-part-2/
Mereka tidak 100% akurat untuk Ember, tetapi konsep inti dan motivasi di balik RunLoop umumnya masih berlaku untuk Ember; hanya beberapa detail penerapan yang berbeda. Tapi, untuk pertanyaan Anda:
Kapan Ember RunLoop dimulai. Apakah itu tergantung pada Router atau Views atau Controllers atau sesuatu yang lain?
Semua peristiwa pengguna dasar (misalnya peristiwa keyboard, peristiwa mouse, dll) akan menjalankan putaran proses. Ini menjamin bahwa perubahan apa pun yang dibuat pada properti terikat oleh peristiwa yang ditangkap (mouse / keyboard / timer / etc) sepenuhnya disebarkan ke seluruh sistem pengikatan data Ember sebelum mengembalikan kontrol kembali ke sistem. Jadi, menggerakkan mouse Anda, menekan sebuah tombol, mengklik tombol, dll., Semua meluncurkan run loop.
kira-kira berapa lama waktu yang dibutuhkan (saya tahu ini agak konyol untuk bertanya dan bergantung pada banyak hal tetapi saya mencari gambaran umum, atau mungkin jika ada waktu minimum atau maksimum yang mungkin diperlukan untuk runloop)
RunLoop tidak akan pernah melacak berapa lama waktu yang dibutuhkan untuk menyebarkan semua perubahan melalui sistem dan kemudian menghentikan RunLoop setelah mencapai batas waktu maksimum; bukan, runloop akan selalu berjalan sampai selesai, dan tidak akan berhenti sampai semua timer kadaluarsa telah dipanggil, binding disebarkan, dan mungkin mereka binding disebarkan, dan sebagainya. Jelas, semakin banyak perubahan yang perlu disebarluaskan dari satu peristiwa, semakin lama RunLoop akan selesai. Berikut adalah contoh (yang sangat tidak adil) tentang bagaimana RunLoop dapat macet dengan menyebarkan perubahan dibandingkan dengan kerangka kerja lain (Backbone) yang tidak memiliki run loop: http://jsfiddle.net/jashkenas/CGSd5/. Moral dari cerita ini: RunLoop sangat cepat untuk sebagian besar hal yang ingin Anda lakukan di Ember, dan di situlah letak sebagian besar kekuatan Ember, tetapi jika Anda ingin menganimasikan 30 lingkaran dengan Javascript pada 60 bingkai per detik, ada mungkin cara yang lebih baik untuk melakukannya daripada mengandalkan Ember's RunLoop.
Apakah RunLoop dijalankan setiap saat, atau hanya menunjukkan periode waktu dari awal hingga akhir eksekusi dan mungkin tidak berjalan untuk beberapa waktu.
Itu tidak dieksekusi setiap saat - itu harus mengembalikan kontrol kembali ke sistem di beberapa titik atau aplikasi Anda akan hang - ini berbeda dari, katakanlah, run loop pada server yang memiliki while(true)
dan terus berjalan selama tak terbatas hingga server mendapat sinyal untuk dimatikan ... Ember RunLoop tidak memiliki seperti itu while(true)
tetapi hanya berputar sebagai respons terhadap peristiwa pengguna / pengatur waktu.
Jika tampilan dibuat dari dalam satu RunLoop, apakah dijamin bahwa semua kontennya akan masuk ke DOM pada saat loop berakhir?
Mari kita lihat apakah kita bisa mengetahuinya. Salah satu perubahan besar dari SC ke Ember RunLoop adalah, alih-alih mengulang bolak-balik antara invokeOnce
dan invokeLast
(yang Anda lihat di diagram di tautan pertama tentang SproutCore's RL), Ember memberi Anda daftar 'antrian' itu, di dalam putaran putaran, Anda dapat menjadwalkan tindakan (fungsi yang akan dipanggil selama putaran proses) dengan menentukan antrian mana tindakan tersebut (contoh dari sumber:) Ember.run.scheduleOnce('render', bindView, 'rerender');
.
Jika Anda melihat run_loop.js
dalam kode sumber, Anda melihat Ember.run.queues = ['sync', 'actions', 'destroy', 'timers'];
, namun jika Anda membuka debugger JavaScript di browser dalam aplikasi Ember dan mengevaluasi Ember.run.queues
, Anda mendapatkan daftar lengkap dari antrian: ["sync", "actions", "render", "afterRender", "destroy", "timers"]
. Ember menjaga basis kode mereka cukup modular, dan mereka memungkinkan kode Anda, serta kodenya sendiri di bagian terpisah dari pustaka, untuk memasukkan lebih banyak antrian. Dalam hal ini, perpustakaan Ember Views menyisipkan render
dan afterRender
antrian, khususnya setelah actions
antrian. Saya akan membahas mengapa itu mungkin dalam beberapa detik. Pertama, algoritma RunLoop:
Algoritme RunLoop kurang lebih sama seperti yang dijelaskan dalam artikel loop run SC di atas:
- Anda menjalankan kode Anda antara RunLoop
.begin()
dan .end()
, hanya di Ember Anda akan ingin menjalankan kode Anda di dalamnya Ember.run
, yang secara internal akan memanggil begin
dan end
untuk Anda. (Hanya kode run loop internal dalam basis kode Ember yang masih menggunakan begin
dan end
, jadi Anda harus tetap menggunakannya Ember.run
)
- Setelah
end()
dipanggil, RunLoop kemudian memulai untuk menyebarkan setiap perubahan yang dibuat oleh potongan kode yang diteruskan ke Ember.run
fungsi. Ini termasuk menyebarkan nilai properti terikat, menampilkan perubahan tampilan ke DOM, dll. Urutan di mana tindakan ini (mengikat, merender elemen DOM, dll) ditentukan oleh Ember.run.queues
larik yang dijelaskan di atas:
- Run loop akan dimulai pada antrian pertama, yaitu
sync
. Ini akan menjalankan semua tindakan yang dijadwalkan ke dalam sync
antrian dengan Ember.run
kode. Tindakan ini sendiri juga dapat menjadwalkan lebih banyak tindakan yang akan dilakukan selama RunLoop yang sama ini, dan RunLoop berhak untuk memastikan tindakan tersebut melakukan setiap tindakan hingga semua antrean dihapus. Cara melakukannya adalah, di akhir setiap antrean, RunLoop akan memeriksa semua antrean yang sebelumnya dihapus dan melihat apakah ada tindakan baru yang telah dijadwalkan. Jika demikian, itu harus dimulai di awal antrean paling awal dengan tindakan terjadwal yang tidak berkinerja baik dan menghapus antrean, terus melacak langkah-langkahnya dan memulai kembali bila perlu sampai semua antrean benar-benar kosong.
Itulah inti dari algoritme. Begitulah cara data terikat disebarkan melalui aplikasi. Anda dapat mengharapkan bahwa setelah RunLoop berjalan hingga selesai, semua data terikat akan disebarkan sepenuhnya. Lalu, bagaimana dengan elemen DOM?
Urutan antrian, termasuk yang ditambahkan oleh pustaka Ember Views, penting di sini. Perhatikan itu render
dan afterRender
datang setelah sync
, dan action
. The sync
antrian berisi semua tindakan untuk menyebarkan data yang terikat. ( action
, setelah itu, hanya jarang digunakan di sumber Ember). Berdasarkan algoritma di atas, dijamin bahwa pada saat RunLoop mencapai render
antrian, semua data-binding telah selesai disinkronkan. Ini memang disengaja: Anda tidak ingin melakukan tugas mahal untuk merender elemen DOM sebelumnyamenyinkronkan data-binding, karena hal itu kemungkinan akan memerlukan rendering ulang elemen DOM dengan data yang diperbarui - jelas merupakan cara yang sangat tidak efisien dan rawan kesalahan untuk mengosongkan semua antrean RunLoop. Jadi, Ember dengan cerdas menyelesaikan semua pekerjaan pengikatan data yang dapat dilakukan sebelum merender elemen DOM dalam render
antrean.
Jadi, akhirnya, untuk menjawab pertanyaan Anda, ya, Anda dapat mengharapkan bahwa semua rendering DOM yang diperlukan akan terjadi pada saat Ember.run
selesai. Berikut adalah jsFiddle untuk didemonstrasikan: http://jsfiddle.net/machty/6p6XJ/328/
Hal lain yang perlu diketahui tentang RunLoop
Pengamat vs. Pengikatan
Penting untuk diperhatikan bahwa Observers dan Bindings, meskipun memiliki fungsi serupa untuk merespons perubahan dalam properti "watching", berperilaku sangat berbeda dalam konteks RunLoop. Binding propagation, seperti yang telah kita lihat, dijadwalkan ke sync
antrian untuk akhirnya dieksekusi oleh RunLoop. Pengamat, di sisi lain, api segera ketika perubahan properti menyaksikan tanpa harus pertama dijadwalkan ke dalam antrian runloop. Jika Observer dan semua mengikat "watch" properti yang sama, pengamat akan selalu dipanggil 100% dari waktu sebelum pengikatan akan diperbarui.
scheduleOnce
dan Ember.run.once
Salah satu peningkatan efisiensi besar dalam template pembaruan otomatis Ember didasarkan pada fakta bahwa, berkat RunLoop, beberapa tindakan RunLoop yang identik dapat digabungkan ("dihapuskan", jika Anda mau) menjadi satu tindakan. Jika Anda melihat ke run_loop.js
internal, Anda akan melihat fungsi yang memfasilitasi perilaku ini adalah fungsi terkait scheduleOnce
dan Em.run.once
. Perbedaan di antara keduanya tidak begitu penting seperti mengetahui keberadaannya, dan bagaimana mereka dapat membuang tindakan duplikat dalam antrean untuk mencegah banyak kalkulasi yang membengkak dan boros selama putaran proses.
Bagaimana dengan pengatur waktu?
Meskipun 'timer' adalah salah satu antrean default yang tercantum di atas, Ember hanya membuat referensi ke antrean tersebut dalam kasus pengujian RunLoop mereka. Tampaknya antrean seperti itu akan digunakan pada hari-hari SproutCore berdasarkan beberapa deskripsi dari artikel di atas tentang pengatur waktu yang menjadi hal terakhir yang diaktifkan. Di Ember, timers
antrian tidak digunakan. Sebaliknya, RunLoop dapat diputar oleh setTimeout
peristiwa yang dikelola secara internal (lihat invokeLaterTimers
fungsinya), yang cukup cerdas untuk mengulang semua pengatur waktu yang ada, mengaktifkan semua pengatur waktu yang telah kedaluwarsa, menentukan pengatur waktu paling awal di masa mendatang, dan menyetel pengatur waktu internal.setTimeout
hanya untuk peristiwa itu, yang akan menjalankan RunLoop lagi saat diaktifkan. Pendekatan ini lebih efisien daripada meminta setiap pengatur waktu memanggil setTimeout dan membangunkannya sendiri, karena dalam kasus ini, hanya satu panggilan setTimeout yang perlu dibuat, dan RunLoop cukup cerdas untuk mengaktifkan semua pengatur waktu berbeda yang mungkin berbunyi pada saat yang sama waktu.
Lebih lanjut debouncing dengan sync
antrian
Berikut cuplikan dari run loop, di tengah loop melalui semua antrian di run loop. Perhatikan kasus khusus untuk sync
antrian: karena sync
merupakan antrian yang sangat volatil, di mana data disebarkan ke segala arah, Ember.beginPropertyChanges()
dipanggil untuk mencegah pengamat dipecat, diikuti dengan panggilan ke Ember.endPropertyChanges
. Ini bijaksana: jika dalam proses membersihkan sync
antrian, sangat mungkin bahwa properti pada suatu objek akan berubah beberapa kali sebelum bertumpu pada nilai akhirnya, dan Anda tidak ingin menyia-nyiakan sumber daya dengan segera menembakkan pengamat per setiap perubahan .
if (queueName === 'sync')
{
log = Ember.LOG_BINDINGS;
if (log)
{
Ember.Logger.log('Begin: Flush Sync Queue');
}
Ember.beginPropertyChanges();
Ember.tryFinally(tryable, Ember.endPropertyChanges);
if (log)
{
Ember.Logger.log('End: Flush Sync Queue');
}
}
else
{
forEach.call(queue, iter);
}
Semoga ini membantu. Saya pasti harus belajar sedikit hanya untuk menulis hal ini, yang merupakan intinya.