I am implementing multiple ajax calls in sequence in angular. What I want to achieve is working fine with $.ajax but when I implemented with angular where I'm using $http for server request, it's not working. I am wondering that both are returning promise object then why it's not working properly.
Using $.ajax
shoppingList.split(",").reduce(function(resultPromise, shoppingItem) {
return resultPromise.then(function(result) {
pro = $.ajax('/items/'+shoppingItem);
return pro.then(function(res) {
console.log(result);
result.push(new Item(res.label,res.price));
return result;
});
});
}, $.when([])).then(completeCallback);
See the working fiddle - JSFiddle
Using Angular $http
function fetchDataDayWise(dateRanges,completeCallback) {
return dateRanges.reduce(function(resultPromise, dt) {
return resultPromise.then(function(resultData) {
machinePromise = getData();
return machinePromise.then(function(data) {
if(data && data.length > 0)
{
resultData = resultData.concat(data);
}
console.log(resultData);
return resultData;
});
});
}, $.when([])).then(completeCallback);
}
var dateRanges = [1,2,3,4]
function setData(data) {
console.log("data",arguments);
}
fetchDataDayWise(dateRanges,setData);
See the Plunkr
You can see in console. In JSFiddle, you will get array of items while in Angular plunkr it returns object. I guess it's promise object.
Any help would be appreciated.
In your service you use $http.get, this returns a promise which is the object you are seeing, to get your values from it you need to have (in your case):
deferred.resolve(data.data);
instead of
deferred.resolve(data);
Hope this helps. If you have any more questions i'll be happy to answer them.
Update:
This isn't for your code exactly, but it will give you an example of how to achieve sequential calls.
var prevPromise = $q.resolve();
prevPromise = prevPromise.then(function() {
this.someValue= value; //array of databases containing queryresults
return this.function(req, res);
}.bind(this));
I have forked your plnkr hope this will help
http://plnkr.co/edit/VWJMNU?p=preview
function fetchDataDayWise(dateRanges, compleCallback) {
var arrPromise = [];
dateRanges.reduce(function (dt) {
arrPromise.push(getData());
});
$q.all(arrPromise).then(compleCallback);
}
Related
I have a function that calls another function which then does two things:
It does an http get to grab a list of IDs
It then loops through that list of IDs, makes another http get for each ID, and adds the result to the var 'dataList'.
I need to have it return the fully populated dataList as the result, then I can take that list and do something with it. I know I need to use promises for this but I'm having trouble getting the behavior I'm looking for. My latest attempt is below. This does return the expected list, but then it seems to get stuck in a loop - I think it's doing a return for every iteration of the map loop. Any suggestions would be appreciated.
getDataList(searchString, matchCase, rows, start).then(function(result) {
// Do something with result
});
getDataList: function(searchString, matchCase, rows, start) {
let body = {};
let dataList = [];
var url = getUrl();
var defer = $q.defer();
return $http.get(url).success(function(response) {
let dataIdList = [];
body = response.response.docs;
body.map(function getDataId(wfr) {
if (wfr.referenceType === 'data') {
dataIdList.push(wfr.referenceId);
}
});
dataList = dataIdList.map(function getData(dataId) {
dataSvc.getDataDetails(dataId).then(function(response) {
dataList.push(response);
});
});
defer.resolve(dataList);
}).error(function(result) {
defer.reject();
});
}
The .success and .error methods have been removed from the AngularJS framework.1 Avoid using the deferred anti-pattern.2 Use $q.all to resolve multiple AngularJS promises:
getDataList: function(searchString, matchCase, rows, start) {
var url = getUrl();
return $http.get(url).then(function(response) {
let body = response.data.response.docs;
let dataIdList =
body.filter(wfr => wfr.referenceType === 'data').map(_ => _.referenceId);
let dataListPromiseArr = dataIdList.map(dataId => {
return dataSvc.getDataDetails(dataId).then(function(response) {
return response.data;
});
});
return $q.all(dataListPromiseArr);
}).catch(function(response) {
console.log(response);
throw response;
});
}
For more information, see
AngularJS $q Service API Reference - $q.all
I think Promise.all() is what you are looking for.
You can push all the getDataDetails(dataId) requests as promises to an array, then do Promise.all(yourPromiseArray) and return what you want only after Promise.all has successfully finished.
I'd highly recommend looking into async/await during your research, as it could be quite helpful. Plenty of articles on the internet on how you can implement it, and many many StackOverflow questions to look through if you feel like you're stuck with it.
Hope this helps, good luck! :)
communicating with a REST service in ionic, I'ld like to have functions similar to this
function ListCategories_Request(){
rqst=_BuildRequest("ListCategories");
rqst.Data={extraParam1:1,
extraParam2:2
}
return $http({...ValidParameters including the rqst...}).then(GetResult,Request_onError)
}
For each function of the REST-Service, I would build a similar java-script function.
Now, as the sent request is unique, the received result needs to be handled unique, too. The REST-Service-Based analysis of the result is added to the .then-chain, but updating the parent class and the UI needs to happen in the parent class.
So, I would like to do calls like
ListCategories_Request().then(function(res){ UpdateCategories()});
ListFrames_Request().then(function(res){ UpdateFrames()});
The current problem is, that UpdateCategories() is called, before the result of the http-Request is analysed.
So, how do I prevent return $http(...).then(GetResult,OnError) to return, before the specific function inside GetResult is called?
Code of GetResult:
function GetResult( res){
if(res.status==200)
{
if( (res.data!=={}) && (res.data.Data!=={}) )
{
return AnalyzeResult(res.data);
}
}
};
While AnalyzeResult is like:
function AnalyzeResult(Result)
{
Func=Result.Func;
switch(Func.toUpperCase())
{
case "LISTCATEGORIES":
erg = ListCategories_Result(Result);
break;
case "LISTFRAMES":
erg= ListFrames_Result(Result);
break;
default:
erg = {};
console.log("UnKnown Result!");
}
FinalizeRequest(Index);
}
return erg;
}
So, I do not really get, where my mistake is. How do I prevent ListCategories_Request or ListFrames_Request from returning too early?
Thank you and best regards
Frank
You are better off using $q (given you are using angular anyway via ionic). That way you can use promises to do what you want to do. Here's an example that I put together to demonstrate the idea:
var app = angular.module("TestApp",[]);
app.controller("TestController", function($scope, $http, $q){
$scope.message = "Deferred Example";
var deferred = $q.defer();
function getAllPosts(extractor) {
$http.get("https://jsonplaceholder.typicode.com/posts")
.then(function(data){
console.log("Data is: ", data);
$scope.postIds = extractor(data.data);
deferred.resolve($scope.postIds);
});
return deferred.promise;
}
function extractPostIds(data) {
console.log("Exracting the data: ", data);
return data.map(function(post){
return post.id;
})
}
function squarePostIds(postIds) {
console.log("Squaring post ids: ", postIds);
$scope.squaredPostIds = postIds.map(function(id){ return id*id;});
}
getAllPosts(extractPostIds)
.then(function(postIds){
squarePostIds(postIds);
});
});
And here's the JSBin for this: https://jsbin.com/fugeyad/3/edit?html,js,output
Update: Adding some links to read about promises.
http://andyshora.com/promises-angularjs-explained-as-cartoon.html
http://www.html5rocks.com/en/tutorials/es6/promises/
I have done a lot of reading around this, but ultimately the tutorials and guides I have found differ too much for me to get a decent grasp on this concept.
This is what I want to achieve:
1) Simple http request from our server [Any API for demonstration]
2) Run a function with data from (1). [Remove a property from the object]
3) Use result and length of (2) to run a loop of $http requests to our server. [Or any server]
4) This will result in 6 different objects. Run a function on these 6 objects. [Add a property]
5) Once ALL of this is done, run a separate function [Log "finished"]
How can this be achieved using promises? How do I pass data from (1) via a promise to (2)? Is this the right way to achieve what I need to do?
If anyone can show me how this should be structured it would be immensely helpful; I have kept the functions as simple as possible for this question.
Yes, promises are very nice to structure solutions for this kind of problems.
Simplified solution (more or less pseudo-code):
$http(...)
.then(function(response) {
// do something with response, for example:
var list = reponse.data.list;
// return it so that you can use it in the next 'then'.
return list;
})
.then(function(list) {
var promises = [];
angular.forEach(list, function(item) {
// perform a request for each item
var promise = $http(...).then(function(itemResponse) {
itemResponse.extraProperty = true;
return itemResponse;
});
// we make an array of promises
promises.push(promise);
});
// combine all promises into one and return it for the next then()
return $q.all(promises);
})
.then(function(itemsList) {
// itemsList is now an array of all parsed item responses.
console.log(itemsList);
});
(Hopefully this is right, I did not tested it.)
As you can see, you can return values in a callback to pass it to the next then(), or you can pass a promise, and this will result in calling the next callback when it resolves. $q.all() is used to combine multiple promises into one and resolve if all are resolved.
Edit: I realised that you can optionally leave out these three lines:
return list;
})
.then(function(list) {
But it is nice syntax though, because the separation of tasks is more visible.
Check code below, it could contains syntax error, the important is the structure. Step3 contains multiple(6) $http requests, it waits until the last request response to return a unique response object (array) containing response for each $http requets.
//Step 1
var Step1 = function () {
$http.get('api/controller').success(function (resp) {
var object1 = resp;
Step2(object1);
Step3(object1).then(function (resp) {
//resp.data is an array containing the response of each $http request
Step4(resp);
Step5();
});
});
}
//Step2
var Step2 = function(obj){
//do whatever with the object
}
//Step3
var Step3 = function (object1) {
var call = $q.defer();
var get1 = $http.get(object1[0].url);
var get2 = $http.get(object[1].url2);
//...
var get6 = $http.get(object[5].url6);
$q.all([get1, get2,..get6]).then(function (resp) {
call.resolve(resp);
});
return call.promise;
}
//Step4
var Step4 = function (resp) {
for (var i=0; i<resp.data.lenght;i++){
DoWhatEver(resp.data[i]);
};
}
//Step5
var Step5 = function () {
alert("Finished");
}
Step1(); //Call Step1 function
Don't know why you have difficulty implementing this, but maybe $q.all() is what you're missing:
var config1={method:'GET',url:'/api/...'};
$http(config1).success(function(resultsFrom1){
functionForResultsOf1(resultsFrom1);
})
var functionForResultsOf1 = function(resultsOf1){
//remove something from the result, assuming this is a synchronous operation
resultsOf1.splice()...;
var promises=makePromises(*pass whatever you want*);
$q.all(promises).then(function(aggregateOfAllCallsToServer){
angular.forEach(aggregateOfAllCallsToServer,function(data){
//do something to data from each call to the server
})
console.log("finished");
})
}
var makePromises = function(serverUrls){
var promises = [];
angular.forEach(serverUrls, function(url) {
var promise=$http({
url : '/api/'+url,
method: 'GET',
})
promises.push(promise);
});
return $q.all(promises);
}
I'm somewhat confused on how the results return from $q.all should be handled.
Here is my code...
var pageLoad = function () {
var pageLoadPromises = [
youEmployerData.getEmployerPrograms($scope.employerId),
youEmployerData.getWorksites($scope.employerId)
];
$q.all(pageLoadPromises)
.then(function (results) {
$scope.programs = results[0];
$scope.worksites = results[1];
$scope.appLoaded = true;
}, function (r) {
handleResourceError(r);
});
};
Here are my actual resource calls..
you.factory('youEmployerData', function ($resource) {
return {
getEmployerPrograms: function(employerId) {
return $resource("/api/programs/getemployerprograms?employerId=:id").query({ id: employerId });
},
getWorksites: function (employerId) {
return $resource("/api/employer/getworksites?employerId=:id").query({ id: employerId });
}
}
}
I have verified my resource calls work correctly, I just know I'm doing something wrong inside the then(function(results) { - I'm just not quite sure how to handle that data.
Also - $scope.worksites should be an array of worksites that is returned, and $scope.programs an array of programs.
Please let me know if you have any questions or need additional clarification.
Here is the screenshot of console.log(results) - which appears correct, results[0] only have 1 item and results1 ($scope.worksites) - having multiple items, the only problem is something isn't working because the $scope.worksites it not outputting on the screen correctly, its like the array isn't correct or something. Sorry - hard to describe.
Maybe it is because you missed the fact that a $resource object is not a promise? Can you try with:
var pageLoadPromises = [
youEmployerData.getEmployerPrograms($scope.employerId).$promise,
youEmployerData.getWorksites($scope.employerId).$promise
];
I am trying to read data from json and wait until data will be fetched into $scope.urls.content. So I write code:
$scope.urls = { content:null};
$http.get('mock/plane_urls.json').success(function(thisData) {
$scope.urls.content = thisData;
});
And now I am trying to write something like callback but that doesn't work. How can i do that? Or is there any function for this? I am running out of ideas ;/
Do you mean that ?
$http.get('mock/plane_urls.json').success(function(thisData) {
$scope.urls.content = thisData;
$scope.yourCallback();
});
$scope.yourCallback = function() {
// your code
};
You want to work with promises and $resource.
As $http itself returns a promise, all you got to do is to chain to its return. Simple as that:
var promise = $http.get('mock/plane_urls.json').then(function(thisData) {
$scope.urls.content = thisData;
return 'something';
});
// somewhere else in the code
promise.then(function(data) {
// receives the data returned from the http handler
console.log(data === "something");
});
I made a pretty simple fiddle here.
But if you need to constantly call this info, you should expose it through a service, so anyone can grab its result and process it. i.e.:
service('dataService', function($http) {
var requestPromise = $http.get('mock/plane_urls.json').then(function(d) {
return d.data;
});
this.getPlanesURL = function() {
return requestPromise;
};
});
// and anywhere in code where you need this info
dataService.getPlanesURL().then(function(planes) {
// do somehting with planes URL
$scope.urls.content = planes;
});
Just an important note. This service I mocked will cache and always return the same data. If what you need is to call this JSON many times, then you should go with $resource.