#author("2020-06-05T07:04:00+00:00","","")
#topicpath
----


#contents

Angularでプログラム書いていて、サーバからの結果を画面表示するためにコールバックでゴニョゴニョしたり、時間がかかったらタイムアウトしたり、一度だけ取っときゃいいデータはサービスでストアしといたり、一度に複数データを取って全部戻ってきたら処理をしたかったり、そういう非同期関連の処理を調べてて、いろいろメモっとかなきゃとおもいwikiっとく事にしました。。

ってもまだ書き途中ですけど。。

 .controller('Menu7Ctrl', function (sampleRestService1, sharedService1, $scope) {
 .controller('Menu7Ctrl', function (sampleRestService1, $scope) {
     var p4 = sampleRestService1.getWeather();
     console.log(p4);
     p4.then(function (data) {
             $scope.result1 = sampleRestService1.sharedService1.cacheData1;
             $scope.result2 = sampleRestService1.sharedService1.cacheData2;
     p4.then(function (sharedService1) {
             console.log(sharedService1);
             $scope.result1 = sharedService1.get(0);
             $scope.result2 = sharedService1.get(1);
         }, function (data) {
             console.log(data);
         }, function (data) {
             console.log('失敗!');
             console.log(data);
         }
     );
 })

Controllerで非同期通信したい場合は、promiseを返却するサービスを作成して、結果をコールバックでセットするのが一般的(($resourceのgetとかをControllerから呼べば?については後述。))。thenに渡すメソッドは、成功した場合と失敗した場合それぞれの処理ですね。。

このサービス(sampleRestService1) は、サーバにRESTでアクセスしてなんか値を返すとかそんなモノをイメージしてください。

 .factory('sharedService1', function () {
 
     var data1;
     var data2;
 
     // Public API here
     return {
         get cacheData1() {
             return this.data1;
         },
         set cacheData1(val) {
             this.data1 = val;
         },
         get cacheData2() {
             return this.data2;
         },
         set cacheData2(val) {
             this.data2 = val;
         }
     };
 })
 .factory('sampleRestService1', function ($resource, sharedService1, $q) {
         // Resourceの戻り値。
         return {
             get sharedService1() {
                 return sharedService1;
             },
             getWeather: function () {
                 var d = $q.defer();
                 var r1 = $resource('/api/weather1.json');
                 var r2 = $resource('/api/weather2.json',
                     {},
                     {'get': {method: 'GET', timeout: 3500}}
                     // 3.5sでタイムアウトとした
                 );
                 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.cacheData1 = result[0];
                         sharedService1.cacheData2 = result[1];
                         d.resolve(sharedService1);
                         console.log("ホンモノデータを使う。そのあとキャッシュを生成");
                     },
                     function (result) {
                         console.log("$q.allの一つが失敗した");
                         d.reject(result);// $q.all()の失敗を検知してメインのdeferも失敗とする
                     }
                 );
                     $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; [#ge096cae]

$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 [#g67e7230]
続いて複数の非同期処理を実行する場合です。サンプルでは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便利ですね。。


**実行結果をキャッシュする。 [#we2f1d7c]
たとえばサーバから固定的なデータを一度だけ取得するなど、そんなことを考えてみます。このサンプルは ふたつある$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);
などとアクセスしても、キャッシュがあればそれを返すようにすることが出来ます。

いろいろな画面で共通に利用される情報や、プルダウンのデータとか、毎回サーバに問い合わせることナシに、でもログイン時になんでもとっておこうとすると時間かかるし、、みたいな要件で遅延ローディングとしてつかえそうです。





----
この記事は
#vote(おもしろかった,そうでもない)
#vote(おもしろかった[5],そうでもない[0])
- qqq -- [[qq]] &new{2020-06-05 (金) 16:03:53};
- qqq -- [[qq]] &new{2020-06-05 (金) 16:03:51};
- qqq -- [[qq]] &new{2020-06-05 (金) 16:03:50};
- qqq -- [[11]] &new{2020-06-05 (金) 16:04:00};
- qqq -- [[11]] &new{2020-06-05 (金) 16:04:00};

#comment

#topicpath

SIZE(10){現在のアクセス:&counter;}


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS