更新2013年10月9日:実行ループのこのインタラクティブな可視化をチェックアウト:https://machty.s3.amazonaws.com/ember-run-loop-visual/index.html
2013年5月9日更新:以下のすべての基本概念はまだ最新ですが、このコミット以降、Ember Run Loop実装はbackburner.jsと呼ばれる個別のライブラリに分割されましたが、APIのごくわずかな違いがあります。
まず、これらを読んでください:
http://blog.sproutcore.com/the-run-loop-part-1/
http://blog.sproutcore.com/the-run-loop-part-2/
これらはEmberに対して100%正確ではありませんが、RunLoopの背後にある中心的な概念と動機は、依然としてEmberに適用されます。一部の実装の詳細のみが異なります。しかし、あなたの質問に続きます:
Ember RunLoopはいつ起動しますか。ルーター、ビュー、コントローラなどに依存していますか?
すべての基本的なユーザーイベント(キーボードイベント、マウスイベントなど)が実行ループを起動します。これにより、キャプチャされた(マウス/キーボード/タイマー/その他)イベントによってバインドされたプロパティに加えられた変更が、システムに制御を戻す前にEmberのデータバインディングシステム全体に完全に伝達されます。したがって、マウスを動かしたり、キーを押したり、ボタンをクリックしたりすると、すべて実行ループが起動します。
およそどれくらい時間がかかりますか(これは馬鹿げている質問であり、多くのことに依存していることはわかっていますが、一般的なアイデアを探しています。または、実行ループにかかる最小時間または最大時間がある場合)
RunLoopは、すべての変更をシステムに伝達するのにかかる時間を追跡し、最大時間制限に達した後にRunLoopを停止することは決してありません。むしろ、実行ループは常に完了まで実行し、すべての期限切れのタイマーが呼び出されたまでは停止しません、バインディングが伝播し、そしておそらく彼らのバインディングは、というように伝播します。明らかに、単一のイベントから伝達する必要のある変更が多いほど、RunLoopが完了するまでの時間が長くなります。以下は、実行ループのない別のフレームワーク(バックボーン)と比較して、変更の伝播によってRunLoopが行き詰まる(かなり不公平な)例です:http : //jsfiddle.net/jashkenas/CGSd5/。ストーリーの教訓:RunLoopは、Emberでやりたいことがほとんどの場合に非常に高速であり、Emberのパワーの大部分はそこにありますが、毎秒60フレームのJavascriptで30個の円をアニメーション化したい場合は、 EmberのRunLoopに依存するよりも、それを実行するより良い方法かもしれません。
RunLoopは常に実行されていますか、それとも実行の開始から終了までの期間を示しているだけで、しばらくは実行されない場合があります。
これは常に実行されるわけではありません-ある時点でシステムに制御を戻す必要があります。そうしないとアプリがハングします-たとえば、があり、while(true)
無限に続くサーバーでの実行ループとは異なりますサーバーはシャットダウンするシグナルを受け取ります... Ember RunLoopにはそのようなwhile(true)
ものはありませんが、ユーザー/タイマーイベントに応答してのみスピンアップされます。
ビューが1つのRunLoopから作成された場合、ループが終了するまでに、すべてのコンテンツがDOMに組み込まれることが保証されますか?
それを理解できるかどうか見てみましょう。エンバー実行ループへのSCからの大きな変更点の一つは、代わりの間で前後にループで、つまりinvokeOnce
とinvokeLast
(あなたがSproutCoreのRLについての最初のリンクでの図で見た)、エンバーはあなたでは、その「キュー」のリストを提供します実行ループの過程で、アクションが属するキュー(ソースからの例:)を指定することにより、アクション(実行ループ中に呼び出される関数)をスケジュールできますEmber.run.scheduleOnce('render', bindView, 'rerender');
。
あなたが見ればrun_loop.js
、ソースコードでは、あなたが見るEmber.run.queues = ['sync', 'actions', 'destroy', 'timers'];
、まだあなたはエンバーアプリのブラウザでJavaScriptデバッガを開き、評価するならばEmber.run.queues
、あなたはキューの充実リストを取得します:["sync", "actions", "render", "afterRender", "destroy", "timers"]
。Emberはコードベースをかなりモジュール化しており、ライブラリの別の部分にある独自のコードだけでなく、コードもキューを挿入できるようにします。この場合、Ember Viewsライブラリは、特にキューの後に挿入render
およびafterRender
キューしactions
ます。その理由をすぐに説明します。まず、RunLoopアルゴリズム:
RunLoopアルゴリズムは、上記のSC実行ループの記事で説明されているものとほとんど同じです。
- RunLoop
.begin()
との間でコードを実行します。Emberの.end()
場合のみ、代わりEmber.run
に内部でbegin
and end
を呼び出すコードを内で実行します。(Emberコードベースの内部実行ループコードのみが引き続きとを使用begin
しているend
ため、そのまま使用する必要がありますEmber.run
)
end()
が呼び出された後、RunLoopはEmber.run
機能を開始して、関数に渡されたコードのチャンクによって行われたすべての変更を伝達します。これには、バインドされたプロパティの値の伝播、DOMへのビューの変更のレンダリングなどが含まれます。これらのアクション(バインド、DOM要素のレンダリングなど)が実行される順序は、Ember.run.queues
上記の配列によって決まります。
- 実行ループは、最初のキューであるから始まります
sync
。コードsync
によってキューにスケジュールされたすべてのアクションを実行しEmber.run
ます。これらのアクション自体も、この同じRunLoopの間に実行するアクションをさらにスケジュールする可能性があります。すべてのキューがフラッシュされるまで、すべてのアクションを確実に実行するかどうかは、RunLoop次第です。これを行う方法は、すべてのキューの最後に、RunLoopが以前にフラッシュされたすべてのキューを調べ、新しいアクションがスケジュールされているかどうかを確認することです。その場合は、実行されていないスケジュールされたアクションを含む最も早いキューの先頭から開始してキューをフラッシュし、すべてのキューが完全に空になるまで必要に応じてステップを追跡し、最初からやり直す必要があります。
それがアルゴリズムの本質です。これが、バインドされたデータがアプリを介して伝達される方法です。RunLoopの実行が完了すると、バインドされたすべてのデータが完全に伝達されることが期待できます。では、DOM要素についてはどうでしょうか?
ここでは、Ember Viewsライブラリによって追加されたキューを含むキューの順序が重要です。それに気づいてrender
、afterRender
後sync
に来る、そしてaction
。sync
キューがバインドされたデータを伝播するためのすべてのアクションが含まれています。(action
その後、Emberソースで使用されるのはまばらです)。上記のアルゴリズムに基づいて、RunLoopがrender
キューに到達するまでに、すべてのデータバインディングの同期が完了することが保証されています。これは仕様によるものです。DOM要素をレンダリングするという高価なタスクを実行する前に、データバインディングを同期します。更新されたデータでDOM要素を再レンダリングする必要があるためです。明らかに、すべてのRunLoopキューを空にする非常に非効率的でエラーが発生しやすい方法です。したがって、Emberは、render
キュー内のDOM要素をレンダリングする前に、可能なすべてのデータバインディング作業をインテリジェントに実行します。
したがって、最後に、質問に答えるために、はい、必要なDOMレンダリングがEmber.run
終了するまでに行われることを期待できます。ここに示すjsFiddleです:http ://jsfiddle.net/machty/6p6XJ/328/
RunLoopについて他に知っておくべきこと
オブザーバーとバインディング
監視者とバインディングは、「監視された」プロパティの変更に応答する同様の機能を備えていますが、RunLoopのコンテキストではまったく異なる動作をすることに注意することが重要です。これまで見てきたように、バインディングの伝播はsync
キューにスケジュールされ、最終的にはRunLoopによって実行されます。オブザーバーは、他の一方で、火はすぐに監視されるプロパティは、最初の実行ループのキューにスケジュールされることなく、変更したとき。オブザーバーとバインディングがすべて同じプロパティを「監視」する場合、オブザーバーは常に、バインディングが更新される前に100%呼び出されます。
scheduleOnce
そして Ember.run.once
Emberの自動更新テンプレートの大きな効率向上の1つは、RunLoopのおかげで、複数の同一のRunLoopアクションを単一のアクションに合体(「デバウンス」)できるという事実に基づいています。あなたがに見るとrun_loop.js
内部には、この動作を促進する機能は、関連する機能で表示されますscheduleOnce
とEm.run.once
。それらの違いは、それらが存在すること、およびキュー内の重複するアクションを破棄して実行ループ中に大量の無駄な計算が行われないようにする方法ほど重要ではありません。
タイマーはどうですか?
「タイマー」は上記のデフォルトキューの1つですが、EmberはRunLoopテストケースでのみキューを参照します。タイマーが最後に起動するものであるという上記の記事のいくつかの説明に基づいて、このようなキューがSproutCoreの時代に使用されたようです。Emberでは、timers
キューは使用されません。代わりに、RunLoopは、内部で管理されるsetTimeout
イベント(invokeLaterTimers
関数を参照)によってスピンアップできます。これは、既存のすべてのタイマーをループし、期限切れのすべてのタイマーを起動し、最も早い将来のタイマーを決定し、内部タイマーを設定するのに十分インテリジェントですsetTimeout
そのイベントの場合のみ、起動時にRunLoopを再び起動します。この方法では、各タイマーをsetTimeoutで呼び出してそれ自体を起動させるよりも効率的です。この場合、1つのsetTimeout呼び出しを行うだけでよく、RunLoopは、同時に発生する可能性のあるすべての異なるタイマーを起動するのに十分スマートです。時間。
sync
キューでさらにデバウンス
これは、実行ループのすべてのキューを通るループの真ん中にある実行ループのスニペットです。sync
キューの特別なケースに注意してください。これsync
は、データがすべての方向に伝播される特に揮発性のキューEmber.beginPropertyChanges()
が呼び出され、オブザーバーが起動されないようにするために呼び出され、続いてが呼び出されるためEmber.endPropertyChanges
です。これは賢明です:sync
キューをフラッシュする過程で、オブジェクトのプロパティがその最終値に到達する前に複数回変更される可能性があり、1回の変更ごとにオブザーバーをすぐに起動してリソースを無駄にしたくない場合。
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);
}
お役に立てれば。私は間違いなく、このことを書くためだけにかなりのことを学ばなければなりませんでした。