Angularでプログラム書いていて、サーバからの結果を画面表示するためにコールバックでゴニョゴニョしたり、時間がかかったらタイムアウトしたり、一度だけ取っときゃいいデータはサービスでストアしといたり、一度に複数データを取って全部戻ってきたら処理をしたかったり、そういう非同期関連の処理を調べてて、いろいろメモっとかなきゃとおもいwikiっとく事にしました。。 ってもまだ書き途中ですけど。。 .controller('Menu7Ctrl', function (sampleRestService1, $scope) { var p4 = sampleRestService1.getWeather(); console.log(p4); p4.then(function (sharedService1) { console.log(sharedService1); $scope.result1 = sharedService1.get(0); $scope.result2 = sharedService1.get(1); }, function (data) { console.log(data); console.log('失敗!'); } ); }) Controllerで非同期通信したい場合は、promiseを返却するサービスを作成して、結果をコールバックでセットするのが一般的*1。thenに渡すメソッドは、成功した場合と失敗した場合それぞれの処理ですね。。 このサービス(sampleRestService1) は、サーバにRESTでアクセスしてなんか値を返すとかそんなモノをイメージしてください。 .factory('sampleRestService1', function ($resource, sharedService1, $q) { return { getWeather: function () { var d = $q.defer(); if (!(sharedService1.isEmpty(0) || sharedService1.isEmpty(1))) { console.log("キャッシュデータを使う"); d.resolve(sharedService1); } else { var r1 = $resource('/api/weather1.json'); var r2 = $resource('/api/weather2.json', {}, {'get': {method: 'GET', timeout: 3500}} // 3.5sでタイムアウトとした ); $q.all([r1.get().$promise, r2.get().$promise]).then( function (result) { console.log(result[0]); console.log(result[1]); sharedService1.add(result[0]); sharedService1.add(result[1]); d.resolve(sharedService1); console.log("ホンモノデータを使う。そのあとキャッシュを生成"); }, function (result) { console.log("$q.allの一つが失敗した"); d.reject(result);// $q.all()の失敗を検知してメインのdeferも失敗とする } ); } return d.promise; } } } ) .factory('sharedService1', function () { var _data = []; // Public API here return { isEmpty: function (index) { return _data[index] == null; }, get: function (index) { return _data[index]; }, add: function (val) { _data.push(val); } }; }) サービスはこんな感じにしてみました。ふたつのRESTを処理して、返ってきたデータを別のサービス(sharedService1)に格納してます。 ふたつの結果が返ってきてから sharedService1にセットするために 各$resourceのpromiseを使ってしまった(?)ので、 var d = $q.defer(); //長い処理。おわったら、、 d.resolve(); return d.promise; って、新たなpromiseをつくって返してます。。 以下、いろいろなTIPSです 非同期処理を実行する、var d = $q.defer() / d.promise; †$resourceなどでも使用されている、promiseについてです。promiseの仕組みは、非同期処理を行うためにとりあえず呼びだし元にpromiseオブジェクトを返却し、実際に処理が完了したときに、さっき返したpromiseオブジェクトのメソッドを呼び出します。 上の処理でうところの、 var promise = sampleRestService1.getWeather(); promise.then(function (sharedService1) { console.log(sharedService1); $scope.result1 = sharedService1.get(0); $scope.result2 = sharedService1.get(1); }, function (data) { console.log(data); console.log('失敗!'); } ); ココですね。sampleRestService1.getWeather() はとりあえず promiseを返しておいて、実際に処理が完了すると、thenに渡しておいたメソッドが実行されます。一つ目のメソッドは成功時によばれるメソッド、二つ目のメソッドは失敗時によばれるメソッドです。 さて、ではsampleRestService1.getWeather()はどうやって非同期処理を実現しているかですが、中身を見てみると、整理すると以下のようになります。 var d = $q.defer(); いろいろ時間がかかる処理 // 完了できたらresolve d.resolve(sharedService1); // 処理が失敗してしまったら reject d.reject(result); return d.promise; Angularが提供する$qサービスを用いて var d = $q.defer(); という参照を取得し、処理が完了したら d.resolve、失敗してしまったらd.reject をコールするようにしておきます。最後にd.promiseでpromiseを返却します。 複数の非同期処理を実行する、$q.all †続いて複数の非同期処理を実行する場合です。サンプルではRESTでサーバに2回データを取得しに行って、両方とも戻ってきたら処理を進めるようにしています。 具体的には以下の箇所: var r1 = $resource('/api/weather1.json'); var r2 = $resource('/api/weather2.json'); $q.all([r1.get().$promise, r2.get().$promise]).then( function (result) { console.log(result[0]); console.log(result[1]); sharedService1.add(result[0]); sharedService1.add(result[1]); d.resolve(sharedService1); }, function (result) { console.log("$q.allの一つが失敗した"); d.reject(result);// $q.all()の失敗を検知してメインのdeferも失敗とする } ); ココでは r1.get().$promise r2.get().$promise などと $resourceがもっているpromiseオブジェクト(まさにさっきやったヤツ)を複数 $q.all() に渡しています。 $q.all()の戻り値もpromiseオブジェクトになっていて、二つの$resourceが完了するとthenで渡したメソッドがコールバックされます。ちなみに二つ目の失敗時メソッドは、複数のpromiseどれか一つでも失敗したらコールされます。たとえばr2がタイムアウトするなど、そんなケースですね。。 promise便利ですね。。 実行結果をキャッシュする。 †たとえばサーバから固定的なデータを一度だけ取得するなど、そんなことを考えてみます。このサンプルは ふたつある$resourceはそれぞれ一度だけ実行し、その結果をsharedService1 にとっておいて、2回目以降ではそれを返すようにしています。 具体的にはこんな感じ。 sampleRestService1 は sharedService1 をInjectionしていますが、そのsharedService1 は、 .factory('sharedService1', function () { var _data = []; return { isEmpty: function (index) { return _data[index] == null; }, get: function (index) { return _data[index]; }, add: function (val) { _data.push(val); } }; }) とオブジェクトを追加できるようにしてあります。そして、sampleRestService1#getWeatherメソッドは、 getWeather: function () { var d = $q.defer(); if (!(sharedService1.isEmpty(0) || sharedService1.isEmpty(1))) { console.log("キャッシュデータを使う"); d.resolve(sharedService1); } else { var r1 = $resource('/api/weather1.json'); ... 時間がかかる処理。一度だけやるようにしたい d.resolve(sharedService1); } return d.promise; } というように、sharedService1 に値が入っていたらそれを返す、なかったらsharedService1 をつくって、それを返す、というようにしてあります。 sampleRestService1や sharedService1 などAngularのサービスはSingletonなので、ほかのControllerから var result0 = sampleRestService1.sharedService1.get(0); などとアクセスしても、キャッシュがあればそれを返すようにすることが出来ます。 いろいろな画面で共通に利用される情報や、プルダウンのデータとか、毎回サーバに問い合わせることナシに、でもログイン時になんでもとっておこうとすると時間かかるし、、みたいな要件で遅延ローディングとしてつかえそうです。 この記事は
現在のアクセス:3789 |