如何等待,直到响应来自 $http 请求,在 angularjs?

我使用的一些数据是从一个 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);
}

它工作良好,为我按我的要求。 但这里的问题是,当我在我的网页重新加载服务将再次调用和请求服务器。如果在执行依赖于“已定义服务”的其他函数之间,它会给出类似“ something”未定义的错误。所以我想在我的脚本中等待,直到服务加载完成。我该怎么做?在 Angularjs 有这种东西吗?

240782 次浏览

You should use promises for async operations where you don't know when it will be completed. A promise "represents an operation that hasn't completed yet, but is expected in the future." (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise)

An example implementation would be like:

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);
});
}

Edit: Regarding Sujoys comment that What do I need to do so that myFuction() call won't return till .then() function finishes execution.

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.");
}

Well, let's suppose the call to getData() took 10 seconds to complete. If the function didn't return anything in that time, it would effectively become normal synchronous code and would hang the browser until it completed.

With the promise returning instantly though, the browser is free to continue on with other code in the meantime. Once the promise resolves/fails, the then() call is triggered. So it makes much more sense this way, even if it might make the flow of your code a bit more complex (complexity is a common problem of async/parallel programming in general after all!)

for people new to this you can also use a callback for example:

In your service:

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


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

In your controller:

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

I was having the same problem and none if these worked for me. Here is what did work though...

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


return { data: data }
});

and then the function that uses it is...

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.");
});
};

The service isn't that necessary but I think its a good practise for extensibility. Most of what you will need for one will for any other, especially when using APIs. Anyway I hope this was helpful.

FYI, this is using Angularfire so it may vary a bit for a different service or other use but should solve the same isse $http has. I had this same issue only solution that fit for me the best was to combine all services/factories into a single promise on the scope. On each route/view that needed these services/etc to be loaded I put any functions that require loaded data inside the controller function i.e. myfunct() and the main app.js on run after auth i put

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

and in the view I just did

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

in the first/parent view element/wrapper so the controller can run everything inside

myfunct()

without worrying about async promises/order/queue issues. I hope that helps someone with the same issues I had.