Apa itu Ember RunLoop dan bagaimana cara kerjanya?

96

Saya mencoba memahami cara kerja Ember RunLoop dan apa yang membuatnya menarik. Saya telah melihat dokumentasinya , tetapi masih memiliki banyak pertanyaan tentangnya. Saya tertarik untuk memahami lebih baik bagaimana RunLoop bekerja sehingga saya dapat memilih metode yang sesuai dalam ruang namanya, ketika saya harus menunda eksekusi beberapa kode untuk lain waktu.

  • Kapan Ember RunLoop dimulai. Apakah itu tergantung pada Router atau Views atau Controllers atau sesuatu yang lain?
  • 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)
  • Apakah RunLoop dijalankan setiap saat, atau hanya menunjukkan periode waktu dari awal hingga akhir eksekusi dan mungkin tidak berjalan untuk beberapa waktu.
  • Jika tampilan dibuat dari dalam satu RunLoop, apakah dijamin bahwa semua kontennya akan masuk ke DOM pada saat loop berakhir?

Maafkan saya jika ini adalah pertanyaan yang sangat mendasar, saya pikir memahami ini akan membantu pemula seperti saya menggunakan Ember dengan lebih baik.

Aras
sumber
5
Tidak ada dokumen bagus tentang run loop. Saya akan mencoba menyusun slide deck pendek di atasnya minggu ini.
Luke Melia
2
@LukeMelia pertanyaan ini masih sangat membutuhkan perhatian Anda dan sepertinya beberapa orang lain sedang mencari informasi yang sama. Akan luar biasa, jika Anda memiliki kesempatan, untuk berbagi wawasan Anda tentang RunLoop.
Aras

Jawaban:

199

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 invokeOncedan 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.jsdalam 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 renderdan afterRenderantrian, khususnya setelah actionsantrian. 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 begindan enduntuk Anda. (Hanya kode run loop internal dalam basis kode Ember yang masih menggunakan begindan 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.runfungsi. 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.queueslarik yang dijelaskan di atas:
  • Run loop akan dimulai pada antrian pertama, yaitu sync. Ini akan menjalankan semua tindakan yang dijadwalkan ke dalam syncantrian dengan Ember.runkode. 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 renderdan afterRenderdatang setelah sync, dan action. The syncantrian 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 renderantrian, 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 renderantrean.

Jadi, akhirnya, untuk menjawab pertanyaan Anda, ya, Anda dapat mengharapkan bahwa semua rendering DOM yang diperlukan akan terjadi pada saat Ember.runselesai. 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 syncantrian 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.jsinternal, Anda akan melihat fungsi yang memfasilitasi perilaku ini adalah fungsi terkait scheduleOncedan 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, timersantrian tidak digunakan. Sebaliknya, RunLoop dapat diputar oleh setTimeoutperistiwa yang dikelola secara internal (lihat invokeLaterTimersfungsinya), 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.setTimeouthanya 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 syncantrian

Berikut cuplikan dari run loop, di tengah loop melalui semua antrian di run loop. Perhatikan kasus khusus untuk syncantrian: karena syncmerupakan 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 syncantrian, 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.

Alexander Wallace Matchneer
sumber
3
Tulisan yang bagus! Saya mendengar desas-desus bahwa hal "pengamat langsung menembak" mungkin berubah di beberapa titik, untuk membuat mereka tertunda seperti ikatan.
Jo Liss
@ JoLiss ya, saya merasa seperti saya telah mendengar tentang itu selama beberapa bulan ... tidak yakin apakah / kapan itu akan berhasil.
Alexander Wallace Matchneer
1
Brendan Briggs melakukan presentasi yang bagus tentang Run Loop di pertemuan Ember.js NYC bulan Januari 2014. Video di sini: youtube.com/watch?v=iCZUKFNXA0k
Luke Melia
1
Jawaban ini adalah sumber daya terbaik yang saya temukan tentang Ember Run Loop, kerja bagus! Saya baru-baru ini menerbitkan tutorial ekstensif tentang Run Loop berdasarkan pekerjaan Anda yang saya harap menjelaskan lebih banyak detail tentang mekanisme itu. Tersedia di sini di.netguru.co/ember-ebook-form
Kuba Niechciał