Angular $q returning promise multiple $http calls
Solution 1
$q.all
and a map
function are what you need here:
function getSearchData() {
return {
// returns a promise for an object like:
// { abo: resultFromAbo, ser: resultFromSer, ... }
loadDataFromUrls: function () {
var apiList = ["abo", "ser", "vol", "con", "giv", "blo", "par"];
return $q.all(apiList.map(function (item) {
return $http({
method: 'GET',
url: '/api/' + item
});
}))
.then(function (results) {
var resultObj = {};
results.forEach(function (val, i) {
resultObj[apiList[i]] = val.data;
});
return resultObj;
});
}
};
}
Solution 2
If you have an arbitrary set of api calls I would do something like this:
function getSearchData(){
var deferred = $q.defer();
var noOfCalls = apiList.length;
var results = [];
var called = 0;
angular.forEach(apiList, function(item, key) {
$http.get(url).then(function(result){
results.push(result);
called++;
if(called == noOfCalls){
deferred.resolve(results);
}
})
});
return deferred.promise;
}
However if you know what each api call represents its better to use $.all in this way
function search1(){
return $http.get(search1Url).then(function(result){
// do something to it
return result;
});
}
function search2(){
return $http.get(search2Url).then(function(result){
// do something to it
return result;
});
}
function search3(){
return $http.get(search3Url).then(function(result){
// do something to it
return result;
});
}
function search4(){
return $http.get(search4Url).then(function(result){
// do something to it
return result;
});
}
function getSearchResult(){
return $q.all([search1(), search2(), search3(), search4()]).then(function(results){
// OPTIONAL aggregate results before resolving
return results;
});
}
Comments
-
byrdr about 2 years
I'm working on an $http call that loops over each of multiple api's and returns all of the data in one object. I usually have the promise ready to resolve when the $http call has been made. Similar to this:
function getAllData(api) { return $http({ method: 'GET', url: '/api/' + api }) .then(sendResponseData) .catch (sendGetVolunteerError); }
The current function I have loops over each api and pushes each object in the api into an array and then pushes it into an overall array. I had this functioning, returning an multi-dimensional array, which needed to be flattened out.
I'd like to return this in a promise, but am returning
undefined
. Here is what I have so far? Is there a better way to approach this?dataService:
function getSearchData() { return { loadDataFromUrls: function () { var apiList = ["abo", "ser", "vol", "con", "giv", "blo", "par"]; var deferred = $q.defer(); var log = []; angular.forEach(apiList, function (item, key) { var logNew = []; $http({ method: 'GET', url: '/api/' + item }).then(function (response) { angular.forEach(response.data, function (item, key) { this.push(item); }, logNew); return logNew; }); this.push(logNew); }, log); $q.all(log).then( function (results) { deferred.resolve( JSON.stringify(results)) }, function (errors) { deferred.reject(errors); }, function (updates) { deferred.update(updates); }); return deferred.promise; } }; };
Controller:
function getSearchData(){ return dataService.getSearchData.loadDataFromUrls; } $scope.searchData = getSearchData();
-
GregL over 9 yearsDid you mean
$q.defer()
? -
JLRishe over 9 years
-
byrdr over 9 yearsFor some reason I'm returning undefined in the controller: function getSearchData(){ return dataService.getSearchData.loadDataFromUrls; } console.log(getSearchData());
-
JLRishe over 9 years@byrdr That's because
getSearchData
returns an object withloadDataFromUrls
as one of its properties, but you are accessingloadDataFromUrls
as a property ofgetSearchData
. Try:function getSearchData(){ return dataService.getSearchData().loadDataFromUrls; } console.log(getSearchData());
That does leave the question of why you have an extra layer between your data service and yourgetSearchData
function. -
byrdr over 9 yearsThat makes sense, thanks. The above code currently logs the function itself.
-
byrdr over 9 yearswrapping it in an iffe returns a promise Object {then: function, catch: function, finally: function}
-
JLRishe over 9 years@byrdr Yeah, you would need another pair of parentheses to actually call it:
console.log(getSearchData()());
. But I think you're involving too many layers of functions. You can just do:var getSearchData = dataService.getSearchData().loadDataFromUrls; console.log(getSerachData());
. That should log a promise. -
JLRishe over 9 years@byrdr Yes, once you have a promise object, you can call
.then()
on it to access the result. And there's no need to involve IIFEs here. You already have too many function layers, and adding more is not the answer. :) -
Chris Noring over 9 years@JLRishe thanks for pointing out the anti pattern.. I learned something today.. cheers Chris
-
JLRishe over 9 years@Chris Glad to lend a hand. You're still using it in
getSearchResult
, and what you need to pass to$q.all
here issearch1()
,search2()
, etc, not justsearch1
,search2
. And your other functions needreturn
statements because they're not returning anything. -
JLRishe over 9 yearsYou still have an extra return statement in
getSearchResult
, but here's an upvote. :) -
byrdr over 9 yearsEverything is working correctly. thanks for your help.
-
Motoman almost 8 yearsI've been looking for something like this for some time now. Was aware of $q but not map. Works awesome in my factories. Thank You!