DI (Dependency Injection) は Javaや、ASP.NET などバックエンドには人気ですけど、AngularJS はそれを JavaScript に応用できるようにしました。具体的に言うと、モジュールをロードするときや、関数を呼び出す場合、$injector の invoke 関数で、呼び出す対象の引数を全部パースして、providerと instance キャッシュの中に探して行く、もし同じ名前の対象があったら、それを使います。
さらに、Ajax call を実行するとき、$http と $httpBackend を利用することで、$browser も内部で持つことで、すべての操作をライブラリ内部で intercept できるようになります。
この中に、面白いのは $timeout というものです。実際の timeout 関数は既にあるのに、何で $timeout を実装するのか?
理由としては、二つあります。まずは Unit test を書くとき、自分で timeout をトリッガーできるようになります。実際のコードを見ると、promise を使っていますが、promise の中には、window.timeout 関数が使われています。これで timeout を同じ機能が実行できるようになります。さらに、もし外部で同じ promise を resolve したら、同じ関数が呼ばれますので、timeout 発生時と同じ動作になります。これはかなりいいことです。もう timeout を待つ必要がなくなります。
もう一つは Angular Scope の $digest に関係あります。$timeout の中に $rootScope.apply() が呼ばれています。これを理解するには JavaScript 実行のサイクルを理解する必要があります。以前では setTimeout(function() {}, 0) を呼び出すことで、今までの JavaScript コールスタックをすべてクリアした後に、コードを実行する方法があります。$timeout も同じです。AngularJS に自分のサイクル、いわゆる $digest がありますの、この関数ですべての watcher を oldValue, newValue を計算して、listener をその後呼び出します。一つのサイクルを終わらないと、Angular は対象の変更が察知できないことがあります。例えば、ng-include でテンプレートの path を変更したい場合、関数の中に
/* templateUrl の値は abc とします。*/
templateUrl = "";
/* その他の操作*/
templateUrl = "abc"; // テンプレートをリロードしたい
をやると、$digest はこの関数終わった後に実行されますので、templateUrl は変更してないように見えます。つまり、変数の変更をAngularJS に察知させる為に、一つの $digest サイクルの終わりを待つ必要があります。このとき、$timeout は利用すれば、ちゃんと意図通りできます。
templateUrl = "";
/* 他の操作 */
$timeout(function() {templateUrl = "abc"})
これで、最初の templateUrl がクリアされて、$digest の実行で、ng-include directive がクリアされます。その後、次の $digest サイクルで、templateUrl がまた元に戻したので、すべての expression がもう一度更新されます。
もちろん、自分で setTimeout を実行して、scope.apply() を呼び出しても、同じ機能ができますの、そのショートカットとして、$timeout を使えば、簡単になります。
まぁ、dirty check にはいろいろ不利なところもありますが、Knockout.js の set 関数を比べると、自分は Angular のほう好きです。
最近、このライブラリも人気だそうです:
durandal
このライブラリは require.js を使っていますので、Angular でできない遅延ロードもできます。聞いたことですが、テンプレートのほうも Angular よりちょっとだけ優れています。
まぁ、これらは Angular 2.0 で改善される予定なので、少し待ってもいいような気がしますけど。
それでは。
0 件のコメント:
コメントを投稿