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
Change Detection - Google ドキュメント
現時点では Angular.js のパフォーマンス問題は認識されているようです。
design doc を見ると GC の問題もあるようです。
これを解決するために Google は Chrome M35 に Object.observe を実装したという流れのようです。
まとめ
だいぶ Angular.js よりの話なので Backbone.js 側の反論も聞いてみたいと思いました。
思わぬところからいろいろな事がつながって調べてみて良かったです。
新しい ES6 の機能で Node.js や v8 がどのように進化するか楽しみです!!