94

我在多个页面中使用了一些来自 RESTful 服务的数据。所以我为此使用角度工厂。因此,我需要从服务器获取一次数据,并且每次使用该定义的服务获取数据。就像一个全局变量。这是示例:

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中有这样做吗?

4

4 回答 4

151

对于不知道何时完成的异步操作,您应该使用 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 的评论, 我需要做什么才能使 myFuction() 调用在 .then() 函数完成执行之前不会返回。

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() 调用就会被触发。所以这种方式更有意义,即使它可能会使您的代码流更加复杂(毕竟复杂性是异步/并行编程的一个常见问题!)

于 2013-08-24T19:01:22.173 回答
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;
   });
于 2014-07-06T02:59:03.253 回答
1

我遇到了同样的问题,如果这些对我有用,则没有。这是什么工作,虽然......

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 时。无论如何,我希望这会有所帮助。

于 2015-07-06T23:25:15.413 回答
0

仅供参考,这是使用 Angularfire,因此对于不同的服务或其他用途,它可能会有所不同,但应该解决 $http 的相同问题。我有同样的问题,唯一最适合我的解决方案是将所有服务/工厂组合成一个范围内的单一承诺。在需要加载这些服务/等的每个路由/视图上,我将任何需要加载数据的函数放入控制器函数中,即 myfunct() 和主 app.js 在我放入 auth 后运行

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

在我刚刚做的视图中

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

在第一个/父视图元素/包装器中,因此控制器可以运行内部的所有内容

myfunct()

无需担心异步承诺/订单/队列问题。我希望这可以帮助有同样问题的人。

于 2015-11-11T22:31:02.517 回答