dirty check について調べてみた

はじめに

自分は普段 Backbone.js や Angular.js を書く機会はあまりありません。

Backbone.js、Angular.js についてはかじった程度、Ember.js についてはほとんど知りません。

なので理解不足な部分があると思います。よろしければツッコミをお願いします。

この記事の対象となる Angular.js のバージョンは最新の安定版 v1.2.16 です。

この記事の大部分は Angular.js について書かれていますが、基本的に Node.js や JavaScript Engine v8 についての興味から書かれたものです。

tl; dr

ざっくり言うと

  • dirty check とは object の状態を記録して、特定のキッカケを元に変更をチェックする仕組みである

  • パフォーマンスの懸念もあるが、人間の体感速度などを加味し、Angular.js では dirty check を選んだ

  • しかしながら現実的にパフォーマンス問題は存在する

  • Object.observe を利用することで問題を解決したい

ということのようです。

なぜ dirty check が気になったのか

Node.js v0.12 では Promise や Object.observe、WeakMap が使えるようになるらしいです

「こいつはすげー」と思いましたが、「けど Object.observe ってなにが嬉しいの?」と疑問に思い、ちょっと調べてみました。

まずは大津さんの過去のブログからチェックしてみました。

次世代JavaScriptでデータバインディング: Object.observe() を試す - ぼちぼち日記

ここで dirty check という単語にぶつかります。

Accessors vs Dirty-checking

調べてみると以下の記事が見つかりました。

Brendan Graetz • Accessors vs Dirty-checking in Javascript Frameworks

どうやら Angular.js、Ember.js が dirty check を行っているようです。

ご存知の方も多いと思いますが Backbone.js は 専用にラップされたオブジェクトのイベントに Listener を登録することで変更を伝搬させています。

対して Angular.js は dirty check と言われる手法で変更を伝搬しており、Ember.js はこの両方を使えるようです。

Angular.js のドキュメントの Scope Life Cycle を見ると

  • スコープの作成
  • Watcher の登録
  • モデルの変更
  • 変化の観察
  • スコープの破棄

となっていて model の変更時に scope.$apply が呼ばれ(基本的に暗黙的に呼び出される、外部のイベントをハンドリングするには明示的な呼び出しが必要)、scope.$apply の終わりに $digest cycle が実行され、モデルの変化をチェックし、Listener を呼び出します。

https://code.angularjs.org/1.2.16/docs/api/ng/type/$rootScope.Scope#$apply

https://github.com/angular/angular.js/blob/v1.2.16/src/ng/rootScope.js#L581

dirty check の利点

上述の記事 Brendan Graetz • Accessors vs Dirty-checking in Javascript Frameworks によると accessors 方式 (Backbone.js) の場合以下の特徴があります。

  • framework が提供する model オブジェクトをラップしなくてはいけない

  • Listener を正しく発火させるためにプロパティの操作には getter や setter を覚えなければいけない

POJSO (Plain Old JavaScript Object) が扱える dirty checking の方がプロパティのアクセスも Listener の発火も簡単に書けると主張されています。

パフォーマンス

javascript - Databinding in angularjs - Stack Overflow

Angular.js の作者である Misko Hevery が StackOverflow の質問に対して dirty check は十分早いと回答しています

要約すると以下のような事を言っているようです。

  • model をループで順に追加する場合、毎回イベントが発火して view が描画される。UI を一度だけ変更したい場合、これはパフォーマンスを悪化させる。

  • change イベントに対応する Listener が更に change イベントを発生するとスレッドロックのような状態になりかねない

  • 50ms 以内なら人間は早いと感じる

  • 2000 以上の情報を単一のページで見せるのは良い UI とは言えないだろう

しかしながらこの記事は 2012年のものでした。

Backbone.js もこの時点からはだいぶ進化しているかと思われます。

AngularJS 2.0

AngularJS: AngularJS 2.0

Change Detection - Google ドキュメント

現時点では Angular.js のパフォーマンス問題は認識されているようです。

design doc を見ると GC の問題もあるようです。

これを解決するために GoogleChrome M35 に Object.observe を実装したという流れのようです。

まとめ

だいぶ Angular.js よりの話なので Backbone.js 側の反論も聞いてみたいと思いました。

思わぬところからいろいろな事がつながって調べてみて良かったです。

新しい ES6 の機能で Node.js や v8 がどのように進化するか楽しみです!!

参考

AngularJSのパフォーマンス改善入門 - Qiita