Ember RunLoopとは何ですか?


96

私はEmber RunLoopがどのように機能し、何がそれを動かしているのかを理解しようとしています。私が見てきましたドキュメント、まだそれについて多くの疑問を持っています。いくつかのコードの実行を後で延期する必要があるときに、RunLoopがどのように機能するかを理解して、名前空間内で適切なメソッドを選択できるようにしたいと思っています。

  • Ember RunLoopはいつ起動しますか。ルーター、ビュー、コントローラなどに依存していますか?
  • およそどれくらい時間がかかりますか(これは馬鹿げている質問であり、多くのことに依存していることはわかっていますが、一般的なアイデアを探しています。または、実行ループにかかる最小時間または最大時間がある場合)
  • RunLoopは常に実行されていますか、それとも実行の開始から終了までの期間を示しているだけで、しばらくは実行されない場合があります。
  • ビューが1つのRunLoopから作成された場合、ループが終了するまでに、すべてのコンテンツがDOMに組み込まれることが保証されますか?

これらが非常に基本的な質問である場合はご容赦ください。これらを理解することで、Emberをよりよく使用するような初心者に役立つと思います。


5
実行ループに関する優れたドキュメントはありません。今週は短いスライドをまとめてみます。
ルークメリア

2
@LukeMeliaこの質問はまだ必死にあなたの注意を必要とし、他の人が同じ情報を探しているようです。機会があれば、RunLoopに関する洞察を共有していただければ幸いです。
Aras

回答:


199

更新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からの大きな変更点の一つは、代わりの間で前後にループで、つまりinvokeOnceinvokeLast(あなたが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に内部でbeginand 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ライブラリによって追加されたキューを含むキューの順序が重要です。それに気づいてrenderafterRendersyncに来る、そしてactionsyncキューがバインドされたデータを伝播するためのすべてのアクションが含まれています。(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内部には、この動作を促進する機能は、関連する機能で表示されますscheduleOnceEm.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);
}

お役に立てれば。私は間違いなく、このことを書くためだけにかなりのことを学ばなければなりませんでした。


3
素晴らしい記事!「オブザーバーは即座に起動する」ことがバインディングのように遅延させるために、ある時点で変わる可能性があるという噂を聞いています。
Jo Liss、2014年

ええ@JoLiss、私は私が...数ヶ月のためにそれについて聞いたような気がしないでください、それはそれを作ってあげるとき場合/。
アレクサンダーウォレスMatchneer

1
ブレンダンブリッグスは、2014年1月のEmber.js NYCミートアップでRun Loopについて素晴らしいプレゼンテーションを行いました。ビデオはこちら:youtube.com/watch
ルークメリア

1
この回答は、Ember Run Loopについて私が見つけた最高のリソースでした。とても良い仕事でした!私は最近、あなたの研究に基づいてRun Loopに関する広範なチュートリアルを公開しました。そのメカニズムの詳細について説明したいと思います。こちらから利用できますon.netguru.co/ember-ebook-form
KubaNiechciał15
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.