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
];
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! :)
currently I am struggeling a little bit with node.js (I am new to it) doing different API requests (Usabilla API), work on the results and then combine them in order to work on the whole set (e.g. export).
Requesting the API is not the problem but I can't get the results out to do some other stuff on it (asynchronous code drives me crazy).
Attached please find a overview how I thought to do this. Maybe I am totally wrong about this or maybe you have other more elegant suggestions.
My code works until I have to request the two different API "adresses" (they are provided) and then extract the results to do some other stuff.
My problem here is that there are nested functions with a promise and I cant figure out how to pass this through the parent function inside waterfall to get handled by the next function.
In the code, of course there is nothing parallel as shown in the diagram.
Thats another point, how to do that ? Simply nest parallel and series/ another waterfall inside waterfall ?
I am a little bit confused because that gets more and more complex for a simple problem when this would be done with synchronous code.
Here I build up all my request querys (at the moment 4):
function buildQuery(IDs,callback){
var i = 0;
var max = Object.keys(IDs).length;
async.whilst(
function(){return i < max},
function(callback){
FeedbackQuery[i] =
{
identifier: IDs[i].identifier,
query:
{id: IDs[i].id,
params: {since:sinceDate,}
}
};
i++;
callback(null,i);
})
console.log(FeedbackQuery);
callback (null,FeedbackQuery);
};
I then have to decide which type of query it is and add it to an object which should contain all the items of this identifier type:
function FeedbackRequest(FeedbackQuery,callback)
{
var i = 0;
var max = Object.keys(FeedbackQuery).length;
async.whilst(
function(){return i < max},
function (callback){
identifier = FeedbackQuery[i].identifier;
APIquery = FeedbackQuery[i].query;
switch(identifier)
{
case 'mobilePortal':
console.log(FeedbackQuery[i].identifier, 'aktiviert!');
var result = api.websites.buttons.feedback.get(APIquery);
result.then(function(feedback)
{
var item = Object.keys(feedbackResults).length;
feedbackResultsA[item] = feedback;
callback(null, feedbackResultsA);
})
break;
case 'apps':
console.log(FeedbackQuery[i].identifier, 'aktiviert!');
var result = api.apps.forms.feedback.get(APIquery);
result.then(function(feedback)
{
var item = Object.keys(feedbackResults).length;
feedbackResultsB[item] = feedback;
callback(null, feedbackResultsB);
})
break;
}
i++;
callback(null,i);
})
};
Currently the functions are bundled in an async waterfall:
async.waterfall([
async.apply(buildQuery,IDs2request),
FeedbackRequest,
// a function to do something on the whole feedbackResults array
],function (err, result) {
// result now equals 'done'
if (err) { console.log('Something is wrong!'); }
return console.log('Done!');
})
How it actually should be:
Structure
Thank you very much for any tips or hints!
I'm not proficient with async, and I believe if you'r new to this, it's harder than a simple Promise library like bluebird combine with lodash for helpers.
What I would do based on your schemas :
var firstStepRequests = [];
firstStepRequests.push(buildQuery());// construct your first steps queries, can be a loop, goal is to have firstStepRequests to be an array of promise.
Promise.all(firstStepRequests)
.then((allResults) => {
var type1 = _.filter(allResults, 'request_type_1');
var type2 = _.filter(allResults, 'request_type_2');
return {
type1: type1,
type2: type2
};
})
.then((result) => {
result.type1 = //do some work
result.type2 = //do some work
return result;
})
.then((result) => {
//export or merge or whatever.
});
Goal is to have a simple state machine.
UPDATE
If you want to keep identifier for a request, you can use props to have :
var props = {
id_1:Promise,
id_2:Promise,
id_3:Promise
};
Promise.props(props).then((results) => {
// results is {
id_1:result of promise,
id_2:result of promise,
etc...
}
})
You could do something like :
var type1Promises = getType1Requests(); //array of type 1
var type2Promises = getType2Requests(); // array of type 2
var props = {
type_1: Promise.all(type1Promises),
type_2: Promise.all(type2Promises)
}
Promise.props(props).then((result) => {
//result is : {
type_1: array of result of promises of type 1
type_2: array of result of promises of type 2
}
})
I know this has been asked quite a few times already but after a day of search I still don't get it to work, although it's just like what is shown as a solution everywhere...
I have a async request to a database which returns an array of data. For each object in this array I need to start another async request to the database and as soon as ALL of these async requests resolve, I want to return them. I read you could do it with $q.all(...)
So here's the code:
Factory.firstAsyncRequest(id).then(function (arrayWithObjects) {
var promises = [];
var dataArr = [];
angular.forEach(arrayWithObjects, function (object, key) {
var deferred = $q.defer();
promises.push(deferred);
Factory.otherAsyncRequest(key).then(function (objectData) {
dataArr.push({
name: objectData.name,
key: key,
status: objectData.status
});
deferred.resolve();
console.info('Object ' + key + ' resolved');
});
});
$q.all(promises).then(function () {
$rootScope.data = dataArr;
console.info('All resolved');
});});
From the console I see that the $q.all is resolved BEFORE each object. Did I get something wrong? This seems to work for everyone...
Your help is highly appreciated, been looking the whole night, it's 5:30am now lol..
Cheers
EDIT:
So for anyone who's coming here later: It was just the promises.push(deferred.PROMISE) bit. Tho, I read that anguar.forEach is actually not a recommended method to loop through array because it was originally not constructed to be used by the end-user. Don't know if that's correct but I figured out another way if you don't want to use angular.forEach:
Users.getAll(uid).then(function (users) {
var uids = ObjHandler.getKeys(users); //own function just iterating through Object.keys and pushing them to the array
var cntr = 0;
function next() {
if (cntr < uids.length) {
Users.getProfile(uids[cntr]).then(function (profile) {
var Profile = {
name: profile.name,
key: uids[cntr],
status: profile.status
});
dataArr[uids[cntr]] = Profile;
if(cntr===uids.length-1) {
defer.resolve();
console.info('Service: query finished');
} else {cntr++;next}
});
}
}
next();
});
And the getKey function:
.factory('ObjHandler', [
function () {
return {
getKeys: function(obj) {
var r = [];
for (var k in obj) {
if (!obj.hasOwnProperty(k))
continue;
r.push(k)
}
return r
}
};
}])
Instead of
promises.push(deferred);
Try this:
promises.push(deferred.promise);
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);
}
I'm pulling some data from a MongoDB collection using Mongoose. I'm getting back my array of objects with just the fields I've selected. All good. Here's my code:
var fieldsToReturn = 'username password';
User.find({username: {$exists: true}}, fieldsToReturn, {sort: {'created.when': 1}}, function(err, data){
if (err) {
return err;
} else {
// clever code needed here to manipulate the data!
return res.json(data);
}
});
What I want to do is iterate through the array of JavaScript objects returned in data and if there is a password (it'll be a text string) replace password with a boolean true, but if it's null then return a boolean false.
I've tried a bit of underscore.js magic:
_.each(data, function(element, index, list){
if (element.password !== null) {element.password = true};
});
But what I get in the JSON that's returned is "password":"true" and not "password":true. Also tried element.password = new Boolean(true). Any suggestions?
You could use map for this.
data = data.map(function(element) {
if (element.password) {
element.password = true;
}
return element;
});
var data = [
{
password: 'secret'
},
{
password: ''
},
{
password: 'swordfish'
}
];
data = data.map(function(element) {
if (element.password) {
element.password = true;
}
return element;
});
document.querySelector('pre').innerText = JSON.stringify(data);
<pre></pre>
Try using asyncjs lybrary. It will call the callback function when finished all the executions for the array objects. Please, also read carefully about asyncronous flow in javascript (nodejs) .
Hope it helps!!
//this should work if placed on your clever code and installed asyncjs
async.map(data, checkPassword, function(err, results){
//when arrived here, all the array has been checked and modified
console.log(results);
}
//Remember This function must be placed outside of the find() method.
function checkPassword(obj, callback)
{
if (obj.password) {
obj.password = true;
}
return callback(null, obj);
}
It's Mongoose. Something is happening before the data is returned which makes it look like straight JSON but if you try and manipulate the element in a _.each loop then the object you're working on (element) isn't a simple object mapping to the document in MongoDB. Any Mongoose experts out there that can shed light on this?
What I did to fix this is use LoDash's _.mapValues:
function renderUserKeys(obj){
var newObj = _.mapValues(obj, function(n){return n});
if (newObj.password !== null) {newObj.password = true};
return newObj;
};
And then in the code:
...
} else {
var newArray = [];
_.each(data, function(element, index, list){
newArray.push(renderUserKeys(element._doc)); // note the ._doc
});
return res.json(newArray);
};
Maybe there is a better function that _.mapValues (maybe _.merge?) to perform the copy, I'll need to look into this, but for now this works and my Mongoose middleware is still running normally (as I'm not using lean()).
Lesson learned: Mongoose !== Duck. As in, it may look like an object but when you try and manipulate it, it doesn't behave as you expect it to!