AngularJSの約束

ほとんどすべてのWebアプリケーションの主要なコンポーネントの1つは、サーバーとの対話です。 大規模なアプリケーションでは、これは単一の要求とはほど遠いものです。 ただし、クエリは多くの場合、順次実行または並列実行のために組み合わせる必要があり、多くの場合両方を組み合わせます。 さらに、大規模なアプリケーションには通常、多層アーキテクチャがあります。RESTFulAPIのラッパー=>ビジネスエンティティ=>より複雑なビジネスロジック(パーティションは任意です)。 また、各レイヤーで、ある形式のデータを受け入れ、別の形式の次のレイヤーに転送する必要があります。



ここで、Promiseはこれらすべてのタスクに対処するのに役立ちます。



詳細については、猫へようこそ。





Promiseは、完了時間が不明な操作の結果を含むオブジェクトと対話するためのインターフェースを提供します。 最初は、プロミスは解決されず(未解決)、特定の値で解決される(解決される)か、エラーで拒否される(拒否される)。 プロミスが許可または拒否されるとすぐに、その状態は変更できなくなります。これにより、任意の数のチェックで状態が変更されないことが保証されます。 これは、チェックの異なる段階で同じ値を取得することを意味するものではありません。



さらに、Promiseは、順次実行と並列実行の両方で組み合わせることができます。



さらに、説明全体はAngularJS 1.1.5に基づいて構築され、すべての例はテストの形式で実行されます



それでは、約束は何ですか? これは、2つのメソッドを持つオブジェクトです。





AngularJSでは何が約束を返しますか?





次に、約束を処理するためのすべてのオプションを検討します。



最も簡単な使用法





  var responseData; $http.get('http://api/user').then(function(response){ responseData = response.data; });
      
      







面白いことは何もありません-コールバックとすべて。 しかし、プレゼンテーションの何かから始めなければなりませんか?.. :-)



ハンドラーからの戻り値





  var User = function(data){ return angular.extend(this, data); }; $httpBackend.expectGET('http://api/user').respond(200, { result: { data: [{name: 'Artem'}], page: 1, total: 10 } }); var data = {}; $http.get('http://api/user').then(function(response){ var usersInfo = {}; usersInfo.list = _.collect(response.data.result.data, function(u){ return new User(u); }); usersInfo.total = response.data.result.total; return usersInfo; }).then(function(usersInfo){ data.users = usersInfo; });
      
      







この一連のthen



のおかげで、多層アプリケーションを構築できます。 ApiWrapperは要求を作成し、一般的なエラーハンドラーを実行し、応答からのデータを変更せずに次のレイヤーに返しました。 そこで、必要に応じてデータが変換され、次のデータに渡されました。 等



then



からの戻り値はsuccess



callback



-次のthen



callback



。 彼らは何も返しませんでした-未定義はsuccess-callback



(テストを参照)。



reject



$q.reject(value)



を返す必要があります。

  $httpBackend.expectGET('http://api/user').respond(400, {error_code: 11}); var error; $http.get('http://api/user').then( null, function(response){ if (response.data && response.data.error_code == 10){ return { list: [], total: 0 }; } return $q.reject(response.data ? response.data.error_code : null); } ).then( null, function(errorCode){ error = errorCode; } );
      
      







コールチェーン



  $httpBackend.expectGET('http://api/user/10').respond(200, {id: 10, name: 'Artem', group_id: 1}); $httpBackend.expectGET('http://api/group/1').respond(200, {id: 1, name: 'Some group'}); var user; $http.get('http://api/user/10').then(function(response){ user = response.data; return $http.get('http://api/group/' + user.group_id); }).then(function(response){ user.group = response.data; });
      
      





これにより、ピラミッド型のコードが回避され、コードがより線形になり、したがって読みやすくなり、保守が容易になります。



すべてを期待したリクエストの並列実行



$q.all(...)



は、promiseオブジェクトの配列または辞書を受け入れ、それらをすべてのpromiseが解決されたときに解決されるか、少なくとも1つのpromiseが拒否されたときにエラーで拒否されるものに結合します。 この場合、値は、 all



メソッドがどのように呼び出されたかに応じて、配列または辞書として成功コールバックに送られます。



  $httpBackend.expectGET('http://api/obj1').respond(200, {type: 'obj1'}) var obj1, obj2; var request1 = $http.get('http://api/obj1'); var request2 = $timeout(function(){ return {type: 'obj2'}; }); $q.all([request1, request2]).then(function(values){ obj1 = values[0].data; obj2 = values[1]; }); expect(obj1).toBeUndefined(); expect(obj2).toBeUndefined(); $httpBackend.flush(); expect(obj1).toBeUndefined(); expect(obj2).toBeUndefined(); $timeout.flush(); expect(obj1).toEqual({type: 'obj1'}); expect(obj2).toEqual({type: 'obj2'});
      
      







  $q.all({ obj1: $http.get('http://api/obj1'), obj2: $timeout(function(){ return {type: 'obj2'}; }) }).then(function(values){ obj1 = values.obj1.data; obj2 = values.obj2; });
      
      







$ q.when



promiseオブジェクトでオブジェクトをラップします。 ほとんどの場合、ユニットテストのmokaに必要です。 または、クエリのチェーンがある場合、そのうちの1つを常に実行する必要はなく、一部のケースでのみ実行する必要があり、他のケースでは既製のオブジェクトがあります。



  spyOn(UserApi, 'get').andReturn($q.when({id: 1, name: 'Artem'})); var res; UserApi.get(1).then(function(user){ res = user; }); $rootScope.$digest(); expect(res).toEqual({id: 1, name: 'Artem'});
      
      







約束を解決するには、少なくとも1 $digest



サイクルを完了する必要があることに注意してください。



deferred



オブジェクトを作成します



$q



サービスを使用すると、遅延オブジェクトの非同期操作を対応するpromiseオブジェクトでラップすることもできます。

  var postFile = function(name, file) { var deferred = $q.defer(); var form = new FormData(); form.append('file', file); var xhr = new XMLHttpRequest(); xhr.open('POST', apiUrl + name, true); xhr.onload = function(e) { if (e.target.status == 200) { deferred.resolve(); } else { deferred.reject(e.target.status); } if (!$rootScope.$$phase) $rootScope.$apply(); }; xhr.send(form); return deferred.promise; };
      
      







ここでのキーポイント:





AngularJSの機能



まず、AngularJSのダーティチェックを考慮して$ qサービスが実装されます。これにより、解決と拒否が高速になり、ブラウザによる不要な再描画が削除されます。

次に、角度式を補間および評価する場合、promiseオブジェクトは解決後に取得された値として解釈されます。

  $rootScope.resPromise = $timeout(function(){ return 10; }); var res = $rootScope.$eval('resPromise + 2'); expect(res).toBe(2); $timeout.flush(); res = $rootScope.$eval('resPromise + 2'); expect(res).toBe(12);
      
      







つまり、文字列watch



を使用して変数の変更を監視すると、解決された値が値として取得されます。

  var res; $rootScope.resPromise = $timeout(function(){ return 10; }); $rootScope.$watch('resPromise', function(newVal){ res = newVal; }); expect(res).toBeUndefined(); $timeout.flush(); expect(res).toBe(10);
      
      





例外は、関数の使用です。 それから返される値はそのまま使用されます。 promiseオブジェクトになります。

  $rootScope.resPromise = function(){ return $timeout(function(){ return 10; }); }; var res = $rootScope.$eval('resPromise()'); expect(typeof res.then).toBe('function');
      
      







これを理解することは、さまざまな読み込みウィジェット、ボタンのディレクティブを読み込むなどの場合に重要です。



AngularJS 1.2.0-Promiseの新機能







そして最後に、WebStorm 7 EAPから実行されたテストのスクリーンショット。 それにもかかわらず、彼らはカルマとの素晴らしい統合を追加しました。








All Articles