angularjsで、応答が$ httpリクエストから来るまで待つ方法は?


93

複数のページでRESTfulサービスからのデータを使用しています。だから私はそのために角度工場を使用しています。そのため、サーバーからデータを1回取得する必要があり、その定義されたサービスでデータを取得するたびに取得しました。グローバル変数のように。これがサンプルです:

var myApp =  angular.module('myservices', []);

myApp.factory('myService', function($http) {
    $http({method:"GET", url:"/my/url"}).success(function(result){
        return result;
    });
});

私のコントローラーでは、このサービスを次のように使用しています:

function myFunction($scope, myService) {
    $scope.data = myService;
    console.log("data.name"+$scope.data.name);
}

私の要件に応じて、それは私にとってはうまくいきます。しかし、ここでの問題は、ウェブページにリロードすると、サービスが再び呼び出され、サーバーを要求することです。「定義されたサービス」に依存する他の関数が実行されると、「何か」が未定義のようなエラーが発生します。したがって、サービスが読み込まれるまでスクリプトで待機したいと思います。どうやってやるの?とにかくそれはangularjsでそれをしますか?

回答:


150

いつ完了するかわからない非同期操作では、promiseを使用する必要があります。約束は、「まだ完了していないが、将来的に予想される操作を表します」。(https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise

実装例は次のようになります。

myApp.factory('myService', function($http) {

    var getData = function() {

        // Angular $http() and then() both return promises themselves 
        return $http({method:"GET", url:"/my/url"}).then(function(result){

            // What we return here is the data that will be accessible 
            // to us after the promise resolves
            return result.data;
        });
    };


    return { getData: getData };
});


function myFunction($scope, myService) {
    var myDataPromise = myService.getData();
    myDataPromise.then(function(result) {  

       // this is only run after getData() resolves
       $scope.data = result;
       console.log("data.name"+$scope.data.name);
    });
}

編集:Sujoysについて、 .then()関数の実行が完了するまでmyFuction()呼び出しが返らないようにするために何をする必要があるについてコメントします。

function myFunction($scope, myService) { 
    var myDataPromise = myService.getData(); 
    myDataPromise.then(function(result) { 
         $scope.data = result; 
         console.log("data.name"+$scope.data.name); 
    }); 
    console.log("This will get printed before data.name inside then. And I don't want that."); 
 }

さて、getData()の呼び出しが完了するまでに10秒かかったとしましょう。その時間内に関数が何も返さなかった場合、それは事実上通常の同期コードになり、完了するまでブラウザーをハングさせます。

約束はすぐに戻るので、ブラウザはその間他のコードを続行できます。promiseが解決または失敗すると、then()呼び出しがトリガーされます。したがって、コードのフローが少し複雑になる場合でも、この方法の方がはるかに理にかなっています(結局のところ、複雑さは非同期/並列プログラミングの一般的な問題です!)


2
これは私の問題を解決しました!!! 他の誰にとっても、私はajax呼び出しからのデータを必要とするドロップダウンを持っていたので、スコープが作成されたとき、データは利用できませんでした。この延期により、スコープを割り当てて、ajax呼び出しからのデータを取得できます。
Kat Lim Ruiz

8
@mikel:ここで別の質問があります。myFuction()呼び出しはすぐに戻りますが、promise .then()は後で呼び出されます。.then()関数が実行を完了するまでmyFuction()呼び出しが返らないようにするために何をする必要がありますか?function myFunction($scope, myService) { var myDataPromise = myService.getData(); myDataPromise.then(function(result) { $scope.data = result; console.log("data.name"+$scope.data.name); }); console.log("This will get printed before data.name inside then. And I don't want that."); }
Sujoy

13

これを初めて使う人のために、例えばコールバックを使うこともできます:

あなたのサービスで:

.factory('DataHandler',function ($http){

   var GetRandomArtists = function(data, callback){
     $http.post(URL, data).success(function (response) {
         callback(response);
      });
   } 
})

コントローラで:

    DataHandler.GetRandomArtists(3, function(response){
      $scope.data.random_artists = response;
   });

素晴らしいソリューション。私がそれを調べていたとき、同じ線に沿って考えていました。誰かがこれをそこに出してくれてうれしい。
2017年

0

ちなみに、これはAngularfireを使用しているため、別のサービスや他の用途では少し異なる場合がありますが、$ httpと同じ問題を解決する必要があります。私と同じ問題のみの解決策がありましたが、すべてのサービス/ファクトリーを組み合わせて、スコープに関する単一の約束をすることが最善でした。これらのサービスなどをロードする必要のある各ルート/ビューで、コントローラー関数内にデータのロードを必要とするすべての関数を配置します。

myservice.$loaded().then(function() {$rootScope.myservice = myservice;});

そして私がやったビューでは

ng-if="myservice" ng-init="somevar=myfunct()"

コントローラがすべてを内部で実行できるように、最初の/親のビュー要素/ラッパー

myfunct()

非同期の約束/注文/キューの問題を心配することなく。私が持っていた同じ問題を持つ誰かを助けることを願っています。


0

私は同じ問題を抱えていましたが、これらが私にとってうまくいったのであれば何もありませんでした。これがうまくいったことです...

app.factory('myService', function($http) {
    var data = function (value) {
            return $http.get(value);
    }

    return { data: data }
});

そしてそれを使用する関数は...

vm.search = function(value) {

        var recieved_data = myService.data(value);

        recieved_data.then(
            function(fulfillment){
                vm.tags = fulfillment.data;
            }, function(){
                console.log("Server did not send tag data.");
        });
    };

サービスはそれほど必要ではありませんが、拡張性のための良い習慣だと思います。特にAPIを使用する場合は、必要なもののほとんどが他のものに必要です。とにかくこれがお役に立てば幸いです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.