Güncelleme 10/9/2013: Çalıştırma döngüsünün bu etkileşimli görselleştirmesine göz atın: https://machty.s3.amazonaws.com/ember-run-loop-visual/index.html
Güncelleme 5/9/2013: Aşağıdaki tüm temel kavramlar hala güncel, ancak bu işlemle birlikte Ember Run Loop uygulaması, bazı çok küçük API farklılıkları ile backburner.js adlı ayrı bir kitaplığa ayrıldı .
Öncelikle şunları okuyun:
http://blog.sproutcore.com/the-run-loop-part-1/
http://blog.sproutcore.com/the-run-loop-part-2/
Ember için% 100 doğru değiller, ancak RunLoop'un arkasındaki temel kavramlar ve motivasyon hala genellikle Ember için geçerlidir; yalnızca bazı uygulama ayrıntıları farklılık gösterir. Ancak sorularınıza gelince:
Ember RunLoop ne zaman başlar. Yönlendiriciye veya Görünümlere veya Denetleyicilere veya başka bir şeye bağlı mı?
Tüm temel kullanıcı olayları (örn. Klavye olayları, fare olayları, vb.) Çalışma döngüsünü başlatacaktır. Bu, yakalanan (fare / klavye / zamanlayıcı / vb.) Olay tarafından bağlı özelliklerde yapılan her türlü değişikliğin, kontrolü sisteme geri döndürmeden önce Ember'in veri bağlama sistemi boyunca tamamen yayılmasını garanti eder. Böylece, farenizi hareket ettirmek, bir tuşa basmak, bir düğmeye tıklamak, vb. Hepsi çalışma döngüsünü başlatır.
yaklaşık olarak ne kadar sürer (Bunun birçok şeye sormak için oldukça saçma olduğunu ve bağımlı olduğunu biliyorum, ancak genel bir fikir arıyorum veya belki bir runloop'un alabileceği minimum veya maksimum süre varsa)
Hiçbir noktada RunLoop, tüm değişiklikleri sisteme yaymanın ne kadar zaman aldığını takip edemez ve maksimum zaman sınırına ulaştıktan sonra RunLoop'u durdurmaz; daha ziyade, RunLoop her zaman tamamlanana kadar çalışır ve süresi dolan tüm zamanlayıcılar çağrılana, bağlamalar yayılıncaya ve belki de bağları yayılıncaya kadar durmaz . Açıktır ki, tek bir olaydan ne kadar fazla değişiklik yapılması gerekiyorsa, RunLoop'un bitmesi o kadar uzun sürecektir. İşte RunLoop'un, çalışma döngüsü olmayan başka bir çerçeveye (Backbone) kıyasla ilerleyen değişikliklerle nasıl tıkanabileceğine dair (oldukça haksız) bir örnek: http://jsfiddle.net/jashkenas/CGSd5/. Hikayenin ahlaki: RunLoop, Ember'da yapmak isteyeceğiniz çoğu şey için gerçekten hızlıdır ve Ember'in gücünün çoğunun yattığı yerdir, ancak kendinizi Javascript ile saniyede 60 kare hızında 30 daireyi canlandırmak istediğinizde bulursanız, orada Ember's RunLoop'a güvenmekten daha iyi bir yol olabilir.
RunLoop her zaman yürütülüyor mu, yoksa sadece uygulamanın başından sonuna kadar bir süreyi mi gösteriyor ve bir süre çalışmayabilir.
Her zaman çalıştırılmaz - kontrolü bir noktada sisteme geri döndürmesi gerekir, yoksa uygulamanız askıda kalır - diyelim ki, a'ya sahip olan while(true)
ve sonsuza kadar devam eden bir sunucudaki çalışma döngüsünden farklıdır. sunucu kapatma sinyali alır ... Ember RunLoop'ta böyle bir şey yoktur while(true)
, ancak yalnızca kullanıcı / zamanlayıcı olaylarına yanıt olarak döndürülür.
Bir görünüm bir RunLoop içinden oluşturulursa, tüm içeriğinin döngü sona erdiğinde DOM'a gireceği garanti edilir mi?
Bakalım bunu çözebilecek miyiz. SC'den Ember RunLoop'a en büyük değişikliklerden biri, invokeOnce
ve invokeLast
(SproutCore'un RL'si hakkındaki ilk bağlantıdaki şemada gördüğünüz) arasında ileri geri döngü yapmak yerine , Ember size Bir çalıştırma döngüsünün seyri, eylemin hangi kuyruğa ait olduğunu belirterek (örneğin kaynaktan:) eylemleri (çalışma döngüsü sırasında çağrılacak işlevler Ember.run.scheduleOnce('render', bindView, 'rerender');
) programlayabilirsiniz.
Eğer bakarsak run_loop.js
kaynak kodunda, gördüğünüz Ember.run.queues = ['sync', 'actions', 'destroy', 'timers'];
bir Kor uygulamasında tarayıcıda JavaScript ayıklayıcısını açıp değerlendirmek henüz eğer Ember.run.queues
sen kuyruklar daha dolgun listesini almak,: ["sync", "actions", "render", "afterRender", "destroy", "timers"]
. Ember, kod tabanını oldukça modüler tutar ve kodunuzun yanı sıra kendi kodunun kitaplığın ayrı bir bölümünde daha fazla kuyruk eklemesini mümkün kılar. Bu durumda, Ember Views kitaplığı , özellikle kuyruktan sonra ekler render
ve afterRender
sıralar actions
. Bunun neden bir saniye içinde olabileceğini anlayacağım. İlk olarak, RunLoop algoritması:
RunLoop algoritması, yukarıdaki SC çalıştırma döngüsü makalelerinde açıklananla hemen hemen aynıdır:
- Sen RunLoop arasında kod çalıştırmasına
.begin()
ve .end()
sadece Ember içinde yerine sonunda kodunuzu çalıştırmak isteyeceksiniz Ember.run
içten arayacak olan begin
ve end
senin için. (Yalnızca Ember kod tabanındaki dahili çalıştırma döngüsü kodu hala kullanır begin
ve end
bu nedenle sadece bağlı kalmalısınız Ember.run
)
- Sonra
end()
denir RunLoop sonra vitese başladı geçirilen kod yığın tarafından yapılan her değişikliği yaymak için Ember.run
işlevi. Bu, ilişkili özelliklerin değerlerinin yayılmasını, görünüm değişikliklerinin DOM'da oluşturulmasını, vb. İçerir. Bu işlemlerin (bağlama, DOM öğelerini oluşturma, vb.) Gerçekleştirilme Ember.run.queues
sırası, yukarıda açıklanan dizi tarafından belirlenir :
- Çalıştırma döngüsü, olan ilk kuyrukta başlayacaktır
sync
. Kod sync
tarafından sıraya planlanan tüm eylemleri çalıştırır Ember.run
. Bu eylemlerin kendileri de aynı Çalıştırma Döngüsü sırasında gerçekleştirilecek daha fazla eylem planlayabilir ve tüm kuyruklar temizlenene kadar her eylemi gerçekleştirmesini sağlamak Çalıştırma Döngüsüne bağlıdır. Bunu yapmanın yolu, her kuyruğun sonunda, Çalıştırma Döngüsü önceden temizlenmiş tüm kuyruklara bakacak ve herhangi bir yeni eylemin planlanıp programlanmadığını kontrol edecektir. Öyleyse, en erken sıranın başlangıcında, yerine getirilmemiş zamanlanmış eylemlerle başlamalı ve kuyruğu boşaltmalı, adımlarını izlemeye devam etmeli ve tüm kuyruklar tamamen boşalana kadar gerektiğinde baştan başlamalıdır.
Algoritmanın özü budur. Bağlı veriler bu şekilde uygulama aracılığıyla yayılır. Bir Çalıştırma Döngüsü tamamlandıktan sonra, tüm bağlı verilerin tamamen yayılmasını bekleyebilirsiniz. Peki, DOM öğeleri ne olacak?
Ember Views kitaplığı tarafından eklenenler de dahil olmak üzere kuyrukların sırası burada önemlidir. Bunu fark et render
ve afterRender
sonra gel sync
ve action
. sync
Sıra ciltli verileri çoğaltılmasına yönelik tüm eylemleri içerir. ( action
bundan sonra Ember kaynağında yalnızca seyrek olarak kullanılır). Yukarıdaki algoritmaya bağlı olarak, RunLoop kuyruğa geldiğinde render
tüm veri bağlamalarının senkronizasyonu bitireceği garanti edilir . Bu tasarım gereğidir: DOM öğelerini oluşturma gibi pahalı bir görevi daha önce gerçekleştirmek istemezsiniz.Veri bağlamalarını senkronize etmek, çünkü bu muhtemelen DOM öğelerini güncellenmiş verilerle yeniden oluşturmayı gerektirecektir - açıkçası tüm RunLoop kuyruklarını boşaltmanın çok verimsiz ve hataya açık bir yolu. Böylece Ember, render
kuyruktaki DOM öğelerini oluşturmadan önce yapabileceği tüm veri bağlama işlerini akıllıca kullanır .
Son olarak, sorunuzu yanıtlamak için, evet, gerekli tüm DOM işlemelerinin Ember.run
bitiş saatine kadar gerçekleşmiş olmasını bekleyebilirsiniz . İşte size gösterilecek bir jsFiddle: http://jsfiddle.net/machty/6p6XJ/328/
RunLoop hakkında bilinmesi gereken diğer şeyler
Gözlemciler ve Bağlamalar
Gözlemciler ve Bağlamaların, "izlenen" bir özellikteki değişikliklere yanıt verme gibi benzer işlevselliğe sahip olsalar da, bir Çalıştırma Döngüsü bağlamında tamamen farklı davrandıklarını unutmamak önemlidir. Bağlama yayılımı, gördüğümüz gibi, sync
sonunda RunLoop tarafından yürütülecek şekilde kuyruğa programlanır . Öte yandan, gözlemciler, izlenen özellik değiştiğinde, ilk olarak bir RunLoop kuyruğuna programlanmasına gerek kalmadan hemen ateş ederler. Bir Gözlemci ve bir bağın tümü aynı özelliği "izlerse", gözlemci her zaman bağlanmanın güncellenmesinden% 100 önce çağrılacaktır.
scheduleOnce
ve Ember.run.once
Ember'ın otomatik güncelleme şablonlarındaki en büyük verimlilik artışlarından biri, RunLoop sayesinde, birden fazla özdeş RunLoop eyleminin tek bir eylemde birleştirilebilmesi (eğer isterseniz "iptal edilmesi") gerçeğine dayanmaktadır. run_loop.js
İç kısımlara bakarsanız , bu davranışı kolaylaştıran işlevlerin ilgili işlevler olduğunu göreceksiniz scheduleOnce
ve Em.run.once
. Aralarındaki fark, var olduklarını bilmek ve çalışma döngüsü sırasında çok fazla şişkin, israflı hesaplamayı önlemek için sıradaki yinelenen eylemleri nasıl atabilecekleri kadar önemli değildir.
Peki ya zamanlayıcılar?
'Zamanlayıcılar' yukarıda listelenen varsayılan kuyruklardan biri olsa da, Ember yalnızca RunLoop test durumlarında kuyruğa başvurur. Öyle görünüyor ki, SproutCore günlerinde, ateşlenecek son şey olan zamanlayıcılarla ilgili yukarıdaki makalelerden bazı açıklamalara dayanarak böyle bir kuyruk kullanılmış olacaktı. Ember'da timers
sıra kullanılmaz. Bunun yerine, RunLoop, dahili olarak yönetilen bir setTimeout
olay ( invokeLaterTimers
işleve bakın ) tarafından döndürülebilir ; bu, mevcut tüm zamanlayıcılar arasında döngü yapmak, süresi dolanların tümünü ateşlemek, gelecekteki en erken zamanlayıcıyı belirlemek ve bir dahilisetTimeout
Sadece bu olay için, bu durum RunLoop'u patladığında tekrar döndürür. Bu yaklaşım, her bir zamanlayıcının setTimeout çağrısı yapıp kendini uyandırmasından daha etkilidir, çünkü bu durumda, yalnızca bir setTimeout çağrısının yapılması gerekir ve RunLoop, aynı anda kapanabilecek tüm farklı zamanlayıcıları ateşleyecek kadar akıllıdır. zaman.
İle Daha zıplamaölçer sync
kuyruğunda
Burada, çalışma döngüsündeki tüm kuyruklar boyunca bir döngünün ortasında bulunan çalışma döngüsünden bir parçacık var. sync
Kuyruk için özel duruma dikkat edin : çünkü sync
verilerin her yönde yayıldığı, özellikle uçucu bir kuyruk, Ember.beginPropertyChanges()
herhangi bir gözlemcinin ateşlenmesini önlemek için çağrılır ve ardından bir çağrı yapılır Ember.endPropertyChanges
. Bu akıllıca: sync
Kuyruğu temizleme sırasında, bir nesnedeki bir özelliğin nihai değerine dayanmadan önce birden çok kez değişmesi tamamen olasıdır ve her değişiklik için hemen gözlemcileri kovarak kaynakları israf etmek istemezsiniz. .
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);
}
Bu yardımcı olur umarım. Sadece bu şeyi yazmak için kesinlikle biraz öğrenmem gerekiyordu, bu da mesele buydu.