web-dev-qa-db-ja.com

Ember RunLoopとは何ですか?

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

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

これらが非常に基本的な質問である場合はご容赦ください。これらを理解すると、私のような初心者がEmber.

96
Aras

Update 10/9/2013:実行ループのこのインタラクティブな視覚化を確認してください: 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は常に最後まで実行され、期限切れのタイマーがすべて呼び出され、バインディングが伝搬され、おそらくtheirバインディングが伝搬されるまで停止しません。明らかに、1つのイベントから伝達する必要のある変更が多いほど、RunLoopが完了するまでに時間がかかります。以下は、実行ループを持たない別のフレームワーク(バックボーン)と比較して、変更を伝播することでRunLoopが動かなくなる方法の(かなり不公平な)例です。 http://jsfiddle.net/jashkenas/CGSd5/ 。ストーリーの教訓:RunLoopはEmberでやりたいことのほとんどに本当に高速であり、Emberの力の大部分はそこにありますが、Javascriptで毎秒60フレームで30円をアニメーション化したい場合は、 EmberのRunLoopに依存するよりも、それを実行する方が良い方法かもしれません。

RunLoopは常に実行されていますか、それとも単に実行の開始から終了までの期間を示しており、しばらく実行されない可能性があります。

常に実行されるわけではありません-ある時点で制御をシステムに戻す必要があります。さもないと、アプリがハングします-それは、たとえば、while(true)を持つサーバーでの実行ループとは異なります。サーバーがシャットダウンするシグナルを受け取るまで無限に続きます... Ember RunLoopにはそのようなwhile(true)はありませんが、ユーザー/タイマーイベントに応じてのみスピンアップします。

1つのRunLoop内からビューが作成された場合、ループが終了するまでにすべてのコンテンツがDOMに含まれることが保証されますか?

それを理解できるかどうか見てみましょう。 SCからEmber RunLoopへの大きな変更の1つは、invokeOnceinvokeLast(図に表示されている)の間をループするのではなく、 SproutCoreのRLに関する最初のリンクで)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のみで、代わりにEmber.run内でコードを実行すると、内部でbeginおよびend。 (Emberコードベースの内部実行ループコードのみがbeginおよびendを使用するため、Ember.runのみを使用する必要があります)
  • end()が呼び出された後、RunLoopはギアにキックして、Ember.run関数に渡されたコードチャンクによって行われたすべての変更を伝達します。これには、バインドされたプロパティの値の伝播、DOMへのビューの変更のレンダリングなどが含まれます。これらのアクション(バインディング、DOM要素のレンダリングなど)が実行される順序は、上記のEmber.run.queues配列によって決まります。
  • 実行ループは、最初のキュー(sync)で開始されます。 Ember.runコードによってsyncキューにスケジュールされたすべてのアクションを実行します。これらのアクション自体も、この同じRunLoop中に実行されるアクションをスケジュールすることがあり、すべてのキューがフラッシュされるまですべてのアクションを確実に実行するのはRunLoopの責任です。これを行う方法は、すべてのキューの最後に、RunLoopが以前にフラッシュされたすべてのキューを調べて、新しいアクションがスケジュールされているかどうかを確認することです。その場合、未実行のスケジュール済みアクションで最も早いキューの先頭から開始し、キューをフラッシュし、すべてのキューが完全に空になるまで必要に応じてステップをトレースし、最初からやり直す必要があります。

それがアルゴリズムの本質です。これが、バインドされたデータがアプリを介して伝播される方法です。 RunLoopが完了するまで実行されると、バインドされたすべてのデータが完全に伝播されることが期待できます。それでは、DOM要素についてはどうでしょうか?

ここでは、Ember Viewsライブラリによって追加されたキューを含むキューの順序が重要です。 renderafterRenderは、syncactionの後に来ることに注意してください。 syncキューには、バインドされたデータを伝播するためのすべてのアクションが含まれています。 (その後のactionは、Emberソースでのみ使用されます)。上記のアルゴリズムに基づいて、RunLoopがrenderキューに到達するまでに、すべてのデータバインディングの同期が完了することが保証されます。これは仕様によるものです。DOM要素をレンダリングする高価なタスクを実行したくないでしょう。before更新されたデータでDOM要素を再レンダリングする必要があるため、データバインディングを同期します-明らかに、すべてのRunLoopキューを空にする非常に非効率的でエラーが発生しやすい方法です。したがって、Emberは、renderキュー内のDOM要素をレンダリングする前に、すべてのデータバインディング作業をインテリジェントに処理します。

最後に、質問に答えるために、はい、Ember.runが終了するまでに必要なDOMレンダリングが行われることを期待できます。デモするjsFiddleを次に示します。 http://jsfiddle.net/machty/6p6XJ/328/

RunLoopについて知っておくべき他のこと

オブザーバーとバインディング

ObserversとBindingsは、「監視対象」プロパティの変更に対応する同様の機能を持ちますが、RunLoopのコンテキストではまったく異なる動作をすることに注意することが重要です。これまで見てきたように、バインディングの伝播はsyncキューにスケジュールされ、最終的にRunLoopによって実行されます。一方、オブザーバーは、最初にRunLoopキューにスケジュールする必要なく監視対象のプロパティが変更されると、immediatelyを起動します。オブザーバーとバインディングがすべて同じプロパティを「監視」する場合、オブザーバーはバインディングが更新されるよりも常に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キューをフラッシュする過程で、オブジェクトのプロパティが最終値に置かれる前に複数回変更される可能性が完全にあり、すぐに起動してリソースを無駄にしたくない場合すべての変更ごとにオブザーバー。

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);
}

お役に立てれば。私は間違いなくこのことを書くためにかなり多くのことを学ばなければならなかった、それは一種のポイントでした。