ここで、Promiseはこれらすべてのタスクに対処するのに役立ちます。
詳細については、猫へようこそ。
Promiseは、完了時間が不明な操作の結果を含むオブジェクトと対話するためのインターフェースを提供します。 最初は、プロミスは解決されず(未解決)、特定の値で解決される(解決される)か、エラーで拒否される(拒否される)。 プロミスが許可または拒否されるとすぐに、その状態は変更できなくなります。これにより、任意の数のチェックで状態が変更されないことが保証されます。 これは、チェックの異なる段階で同じ値を取得することを意味するものではありません。
さらに、Promiseは、順次実行と並列実行の両方で組み合わせることができます。
さらに、説明全体はAngularJS 1.1.5に基づいて構築され、すべての例はテストの形式で実行されます 。
それでは、約束は何ですか? これは、2つのメソッドを持つオブジェクトです。
-
then(successCallback, errorCallback)
; -
always(callback)
;
AngularJSでは何が約束を返しますか?
-
$http
-AJAXリクエストを実行するためのサービス。 -
$timeout
-setTimeout
上のAngularJSラッパー。 -
$q
さまざまなメソッド-遅延オブジェクトと約束を作成するためのサービス。
次に、約束を処理するためのすべてのオプションを検討します。
最も簡単な使用法
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; };
ここでのキーポイント:
- 遅延オブジェクトの作成:
var deferred = $q.defer()
- 適切なタイミングでの解決:
deferred.resolve()
- またはエラーの場合は
reject
:deferred.reject(e.target.status)
- 関連するプロミスオブジェクトを
return deferred.promise
ます: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の新機能
-
promise.then(null, errorCallback)
短縮同義語としてのpromise.then(null, errorCallback)
; - Qとの類似性を高めるために、
always(callback)
finally(callback)
名前always(callback)
変更されfinally(callback)
。 - そして最も重要なことは、遅延オブジェクトと約束オブジェクトでの通知のサポートです。これは、進行状況の通知などに使用すると便利です。 これで、完全な
promise.then
署名はそのようにthen(successCallback, errorCallback, progressCallback)
、deferred
notify(progress)
メソッドがthen(successCallback, errorCallback, progressCallback)
deferred
。
そして最後に、WebStorm 7 EAPから実行されたテストのスクリーンショット。 それにもかかわらず、彼らはカルマとの素晴らしい統合を追加しました。