[解決済み] Ember RunLoopとは何か、どのように機能するのか?
質問
EmberのRunLoopがどのように動作し、何がそれを動かすのかを理解しようとしています。私は見てきました ドキュメント を見ましたが、まだそれについて多くの質問があります。私は、いくつかのコードの実行を後で延期しなければならないときに、その名前空間内で適切なメソッドを選択できるように、RunLoopがどのように動作するかをよりよく理解することに興味があります。
- Ember RunLoop はいつ始まるのでしょうか。Router や Views、Controller などに依存するのでしょうか。
- どのくらいの時間がかかりますか (私はこれを尋ねることはむしろ愚かであり、多くのものに依存していることを知っているが、私は一般的なアイデアを探しています、または多分ランループが取るかもしれない最小または最大の時間がある場合)
- RunLoop は常に実行されているのですか、それとも実行の開始から終了までの期間を示しているだけで、しばらくは実行されないかもしれません。
- 1つのRunLoop内からビューが作成された場合、ループが終了するまでにそのすべてのコンテンツがDOMに入ることは保証されていますか?
これらが非常に基本的な質問である場合、私はこれらを理解することが私のようなnoobがより良いEmberを使用するのに役立つと思います。
どのように解決するのですか?
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はいつ始まるのでしょうか。RouterやViews、Controllerなどに依存するのでしょうか?
基本的なユーザーイベント (キーボードイベント、マウスイベントなど) はすべて、ランループを起動します。これは、キャプチャされた (マウス/キーボード/タイマーなど) イベントによってバインドされたプロパティに加えられたいかなる変更も、システムに制御を戻す前に Ember のデータ バインディング システム全体に完全に伝搬されることを保証します。つまり、マウスを動かす、キーを押す、ボタンをクリックするなど、すべて実行ループを起動します。
それはおよそどれくらいかかりますか (私はこれが尋ねるにはかなり愚かで、多くのものに依存していることを知っていますが、私は一般的なアイデアを探しています、または多分、ランループが取るかもしれない最小または最大の時間がある場合)
むしろ、RunLoop は常に完了するまで実行され、期限切れのタイマーがすべて呼び出され、バインディングが伝搬され、そしておそらくは その バインディングが伝搬される、といった具合に。明らかに、1つのイベントから伝搬される必要がある変更が多ければ多いほど、RunLoopが終了するのに時間がかかります。以下は、ランループを持たない別のフレームワーク(Backbone)と比較して、ランループが変更の伝播でどのように泥沼にはまるかの(かなり不公平な)例です。 http://jsfiddle.net/jashkenas/CGSd5/ . しかし、Javascript で 30 個の円を 60 フレーム/秒でアニメーションさせたい場合、Ember の RunLoop に依存するよりも良い方法があるかもしれません。
RunLoopは常時実行されているのか、それとも実行開始から終了までの期間を示しているだけで、しばらくは実行されない可能性があるのか。
常に実行されているわけではありません。ある時点でシステムに制御を戻す必要があり、そうしないとアプリがハングアップします。
while(true)
というように、サーバーがシャットダウンする信号を受け取るまで無限に続くのです。
while(true)
がなく、ユーザーやタイマーのイベントに応じてのみ起動されます。
1つのRunLoop内からビューが作成された場合、ループが終了するまでにそのすべてのコンテンツがDOMに入ることが保証されていますか?
それがわかるかどうか見てみましょう。SCからEmber RunLoopへの大きな変更点の1つは、ループの中で
invokeOnce
と
invokeLast
(を使うことで、Emberは「キュー」のリストを提供し、実行ループの過程で、アクションがどのキューに属するかを指定して、アクション(実行ループ中に呼び出す関数)をスケジュールできます(ソースからの例です)。
Ember.run.scheduleOnce('render', bindView, 'rerender');
).
を見ると
run_loop.js
を見ると、ソースコードに
Ember.run.queues = ['sync', 'actions', 'destroy', 'timers'];
と表示されますが、EmberアプリのブラウザでJavaScriptデバッガを開いて
Ember.run.queues
を評価すると、より詳細なキューのリストが表示されます。
["sync", "actions", "render", "afterRender", "destroy", "timers"]
. Ember はコードベースをかなりモジュール化しており、ライブラリの別の部分にある独自のコードだけでなく、あなたのコードもさらにキューを挿入できるようにしています。この場合、Ember Views ライブラリでは
render
と
afterRender
キュー、特に
actions
キューの後にあります。なぜそうなるかは、後ほど説明します。まず、RunLoopアルゴリズムです。
RunLoopのアルゴリズムは、上記のSCランループの記事で説明したものとほとんど同じです。
-
RunLoop の間でコードを実行する
.begin()
と.end()
の中でコードを実行することになります。Ember.run
の中でコードを実行することになります。begin
と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 要素を レンダリングするという高価なタスクを実行したくないでしょう。そのためには、おそらく更新されたデータで DOM 要素を再レンダリングする必要があり、明らかに非常に非効率でエラーが起こりやすい方法で、すべての RunLoop キューを空にします。そのため、Ember は、データバインディングの作業をできる限りすべて行ってから、DOM 要素を
render
キューに入れます。
最後に、あなたの質問に答えますと、はい、必要な DOM レンダリングは
Ember.run
が終了するまでに、必要な DOM レンダリングが行われると予想できます。以下は、jsFiddleによるデモです。
http://jsfiddle.net/machty/6p6XJ/328/
その他、RunLoopについて知っておくべきこと
オブザーバとバインディングの比較
Observers と Bindings は、"watched" プロパティの変更に応答するという同様の機能を持ちながら、RunLoop のコンテキストでは全く異なる動作をすることに注意することが重要です。バインディングのプロパゲーションは、これまで見てきたように、スケジュール化され
sync
キューにスケジュールされ、最終的に RunLoop によって実行される。一方、オブザーバは
を即座に
一方、オブザーバーは、ウォッチされたプロパティが変更されたときに、最初にRunLoopのキューにスケジュールされることなく、直ちに発火します。Observer とバインディングがすべて同じプロパティを監視する場合、Observer はバインディングが更新されるよりも常に 100% 早く呼ばれることになります。
scheduleOnce
と
Ember.run.once
Ember の自動更新テンプレートにおける大きな効率化のひとつは、RunLoop のおかげで、複数の同一の RunLoop アクションを単一のアクションに合体させることができる ("debounced" とでも言いましょうか) という事実に基づいています。もしあなたが
run_loop.js
内部を見ると、この動作を促進する関数として、関連する関数
scheduleOnce
と
Em.run.once
. これらの違いは、これらの存在を知ること、そして、実行ループ中に多くの肥大化した無駄な計算を防ぐために、キュー内の重複するアクションをどのように破棄することができるかと同じくらい重要ではありません。
タイマーについてはどうですか?
timers」は上記のデフォルトキューの1つであるにもかかわらず、EmberはRunLoopテストケースの中でしかこのキューを参照していません。上記の記事の中で、タイマーが最後に発火するという記述があることから、SproutCore時代にはこのようなキューが使われていたようです。Emberでは
timers
キューは使用されません。その代わり、RunLoopは内部で管理された
setTimeout
イベント (
invokeLaterTimers
関数を参照)、このイベントは既存のタイマーを全てループし、期限が切れたタイマーを全て起動し、最も早い将来のタイマーを決定し、内部の
setTimeout
を設定し、そのイベントが発生したときに再びRunLoopをスピンアップさせます。この場合、1 つの setTimeout 呼び出しを行うだけでよく、RunLoop は同時にオフになる可能性があるすべての異なるタイマーを起動できるほど賢明であるため、この方法は各タイマーに setTimeout を呼び出して自身を起動させるよりも効率的です。
さらにデバウンスするために
sync
キュー
以下は、実行ループの中のすべてのキューを通過するループの途中のスニペットです。の特殊なケースに注意してください。
sync
キューが特殊であることに注意してください。
sync
は特に揮発性の高いキューで、データがあらゆる方向に伝搬されるからです。
Ember.beginPropertyChanges()
が呼び出され、オブザーバが起動されないようにし、その後に
Ember.endPropertyChanges
. これは賢明な方法です。
sync
キューをフラッシュする過程で、オブジェクトのプロパティが最終的な値に落ち着くまでに何度も変更される可能性があり、変更ごとにオブザーバを即座に起動してリソースを無駄にしたくありません。
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);
}
これが役に立つといいのですが。私はこれを書くためにかなりのことを学ばなければなりませんでしたが、それは一種のポイントでした。
関連
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン