如何在AngularJS中等待$http请求的响应?

94

我正在多个页面使用来自RESTful服务的一些数据。 所以我正在使用angular工厂来实现。因此,我需要从服务器获取数据一次,并且每次都使用定义的服务获取数据。就像全局变量一样。这里是一个示例:

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个回答

150

在异步操作中,如果你不知道何时完成,应该使用Promise。一个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);
    });
}

编辑:关于Sujoy的评论,他问:我需要做什么才能使myFunction()调用在.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立即返回,浏览器可以在此期间继续执行其他代码。一旦Promise被解决/失败,then()方法就会被触发。因此,即使这种方式可能使您的代码流程稍微复杂一些(毕竟,复杂性是异步/并行编程的普遍问题!),但这样做更加合理。


2
这解决了我的问题!对于其他人,我有一个需要从 ajax 调用中获取数据的下拉菜单,所以当作用域创建时,数据是不可用的。通过延迟,可以将作用域分配为具有来自 ajax 调用的数据。 - Kat Lim Ruiz
8
@mikel: 我这里有另一个问题。您的myFunction()调用会立即返回,但是那个promise .then()会稍后调用。我需要怎么做才能让myFunction()调用不会在.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("这会在.then内部的数据名称之前被打印出来,我不希望出现这种情况。"); } - 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;
   });

很棒的解决方案。当我在研究它时,也是沿着这个思路考虑的。很高兴有人把它提出来了。 - Nate

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


0

请注意,这是使用Angularfire的,因此对于不同的服务或其他用途可能会有所不同,但应该解决与$http相同的问题。我遇到了同样的问题,唯一适合我的解决方案是将所有服务/工厂组合成单个承诺,并将其放在作用域上。在每个需要加载这些服务/等的路由/视图中,我将需要加载数据的任何函数放在控制器函数内,例如myfunct(),并在身份验证后的主app.js上运行。

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

在视图中,我刚刚做了这件事。
ng-if="myservice" ng-init="somevar=myfunct()"

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

myfunct()

无需担心异步承诺/顺序/队列问题。我希望这能帮助到有同样问题的人。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接