What I want to do : get a list of projects (from API), request the status for each project. And call a callback who should represent the global status of my projects.
How I am trying to do it : the first promise get all projects and in the first then I am trying to call for each project a getProjectStatus who should get the status. I want that my second then wait that I finished the iteration (I tried many different things, but my second then is always called before). I don't know if this is a good using of promises...
var that = this;
var options = { url : this.host + "projects", json : true }
var finalStatus = "SUCCESS";
// first promises
return request(this.options)
.then(function(projects) {
projects.forEach(function(element, index, array) {
// second promises
that.getProjectStatus(element.id, function(status){})
.then(function(status) {
if(status != "SUCCESS)
{
finalStatus = "FAILURE";
}
});
})
})
.then(function(){
callback(finalStatus)});
This should get you going. You just use Promise.all on the array of second promises. I may not have understood the exact syntax of getProjectStatus but you can adjust I hope if needs be
return request(this.options)
.then(function(projects) {
return Promise.all( projects.map(element => that.getProjectStatus(element.id) )
.then(function(){
callback(finalStatus)});
Related
My code is as follows :
myinsts.forEach(function (myinstId) {
Organization.getOrgById(myinstId,function (err,insts)
{
res.json(insts);
})
});
I'm usng Node.js and I'm getting the error "Can't set headers after they are sent" , Obviously my server sends first iteration , how can I make it hold until I get the whole data
It's not going to be pretty. Essentially what you could do is create a variable to hold the data through every iteration and then check if that's the last callback to be called. If it is, then you can output your json. Try something like the following:
var _counter = 0;
var _values = [];
myinsts.forEach(function (myinstId) {
Organization.getOrgById(myinstId,function (err,insts)
{
_values.push(insts);
if(++_counter == myinsts.length)
res.json(_values);
})
});
You would like to use Promises for this kind of works, you can execute each async function in parallel with Promise.all() and get the data in the order that is called.
var promises = [];
myinsts.forEach(function (myinstId) {
promises.push(new Promise((resolve, reject)=>{
Organization.getOrgById(myinstId,function (err,insts){
if(err) return reject(err);
resolve(insts);
});
}));
});
Promise.all(promises)
.then((allInsts)=>{res.json(allInsts)}) // all data fetched in loop order.
.catch((error)=>{console.log(error)});
Also consider to make your getOrgById handler return a Promise instead of using callback.
You have to send response just one time, not several times. Your code send the response each of iteration. Send the response once if specific condition fulfilled. Like below.
myinsts.forEach(function (myinstId, index) {
Organization.getOrgById(myinstId,function (err,insts){
if( index == myinsts.length -1 ){
res.json(insts);
}
})
});
I'm new on AngularJS and JavaScript.
I am getting remote information for each of the elements of an array (cars) and creating a new array (interested prospects). So I need to sync the requests. I need the responses of each request to be added in the new array in the same order of the cars.
I did it first in with a for:
for (a in cars) {
//async request
.then(function () {
//update the new array
});
}
This make all the requests but naturally didn't update the new array.
After seeking in forums, I found this great examples and explanations for returning a intermediate promise and sync all of them.
1. http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
2. http://stackoverflow.com/questions/25605215/return-a-promise-from-inside-a-for-loop
3. http://www.html5rocks.com/en/tutorials/es6/promises/
(#MaurizioIndenmark, #Mark Rajcok , #Michelle Tilley, #Nolan Lawson)
I couldn't use the Promise.resolve() suggested in the second reference. So I had used $q.defer() and resolve(). I guess I have to inject a dependency or something else that I missed. As shown below:
In the Controller I have:
$scope.interestedProspects = [] ;
RequestDetailsOfAsync = function ($scope) {
var deferred = $q.defer();
var id = carLists.map(function (car) {
return car.id;
}).reduce(function (previousValue, currentValue) {
return previousValue.then(function () {
TheService.AsyncRequest(currentValue).then(function (rData) {
$scope.interestedProspects.push(rData);
});
});
}, deferred.resolve());
};
In the Service I have something like:
angular.module('app', []).factory('TheService', function ($http) {
return {
AsyncRequest = function (keyID) {
var deferred = $q.defer();
var promise = authorized.get("somep.provider.api/theService.json?" + keyID).done(function (data) {
deferred.resolve(data);
}).fail(function (err) {
deferred.reject(err);
});
return deferred.promise;
}
}
}
The displayed error I got: Uncaught TypeError: previousValue.then is not a function
I made a jsfiddle reusing others available, so that it could be easier to solve this http://jsfiddle.net/alisatest/pf31g36y/3/. How to wait for AsyncRequests for each element from an array using reduce and promises
I don't know if the mistakes are:
the place where the resolve is placed in the controller function.
the way the reduce function is used
The previousValue and currentValue sometimes are seen by javascript like type of Promise initially and then as a number. In the jsfiddle I have a working example of the use of the reduce and an http request for the example.
Look at this pattern for what you want to do:
cars.reduce(function(promise, car) {
return promise.then(function(){
return TheService.AsyncRequest(car).then(function (rData) {
$scope.details.push(rData);
});
});
}, $q.when());
This will do all the asynchronous calls for every car exactly in the sequence they are in the cars array. $q.all may also be sufficient if the order the async calls are made doesn't matter.
It seems you are calling reduce on an array of ids, but assume in the passed function that you are dealing with promises.
In general, when you want to sync a set of promises, you can use $q.all
You pass an array of promises and get another promise in return that will be resolved with an array of results.
I'm working on a college/personal project which takes RSS urls and sends them through a series of APIs. Code follows:
var tempStory = [];
function getFeed () {
$.getJSON('spoonfed/app/scripts/categories.json', function(categories){
for (var category in categories){
if (categories[category][2] === true){
getFeedURLs(categories[category][0]).then(function(rssData) {
for (var item in rssData){
tempStory.push(rssData[item]);
}
});
}
}
});
}
There are an unknown number of categories I need to iterate over in addition to 10 news stories in each category, which are then pushed to the array tempStory. I need to run another function on this array of data. I think promises are the key here but I can't get my head around how I'd use them in this case.
Any help would be really appreciated and I'm happy to provide more of the code if needed, including the structure of the categories.json file and the getFeedURLs function.
If the issue is that you're just trying to figure out how to get all the data out, then you can do this:
function getFeed() {
return $.getJSON('spoonfed/app/scripts/categories.json').then(function (categories) {
var tempStory = [], promises = [];
for (var category in categories) {
if (categories[category][2] === true) {
promises.push(getFeedURLs(categories[category][0]).then(function (rssData) {
for (var item in rssData) {
tempStory.push(rssData[item]);
}
}));
}
}
return $.when.apply($, promises).then(function() {
// return results array as the fulfilled value of the promise
return tempStory;
});
});
}
getFeed().then(function(data) {
// all the data available here
}, function(err) {
// error here
});
The key aspects of this are:
Return the original ajax promise from getFeed().
When iterating the categories, push all those promises into an array to collect them all
Use $.when() with your array of promises to know when all of those are done
From within the $.getJSON.then() handler, return the $.when() promise which will chain to the $.getJSON() promise and let you return an array of results.
From within the $.when().then() handler, return our results so that becomes the fulfilled value of the final promise
Note: because of the way you chose to accumulate the tempStory array, it's results are not in a guaranteed order. Additional code could be written to maintain the order.
I understand using promises in simple scenarios but currently really confused on how to implement something when using a for loop and some updates to local sqlite database.
Code is as follows
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
var q = $q.defer();
for (var item in surveys) {
var survey = surveys[item];
// created as a closure so i can pass in the current item due to async process
(function(survey) {
ajaxserviceAPI.postSurvey(survey).then(function(response) {
//from response update local database
surveyDataLayer.setLocalSurveyServerId(survey, response.result).then(function() {
q.resolve; // resolve promise - tried only doing this when last record also
})
});
})(survey) //pass in current survey used to pass in item into closure
}
return q.promise;
}).then(function() {
alert('Done'); // This never gets run
});
Any help or assistance would be appreciated. I'm probably struggling on how best to do async calls within loop which does another async call to update and then continue once completed.
at least promises have got me out of callback hell.
Cheers
This answer will get you laid at JS conferences (no guarantees though)
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
return Promise.all(Object.keys(surveys).map(function(key) {
var survey = surveys[key];
return ajaxserviceAPI.postSurvey(survey).then(function(response){
return surveyDataLayer.setLocalSurveyServerId(survey, response.result);
});
}));
}).then(function() {
alert('Done');
});
This should work (explanations in comments):
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
// array to store promises
var promises = [];
for (var item in surveys) {
var survey = surveys[item];
// created as a closure so i can pass in the current item due to async process
(function(survey) {
var promise = ajaxserviceAPI.postSurvey(survey).then(function(response){
//returning this promise (I hope it's a promise) will replace the promise created by *then*
return surveyDataLayer.setLocalSurveyServerId(survey, response.result);
});
promises.push(promise);
})(survey); //pass in current survey used to pass in item into closure
}
// wait for all promises to resolve. If one fails nothing resolves.
return $q.all(promises);
}).then(function() {
alert('Done');
});
Awesome tutorial: http://ponyfoo.com/articles/es6-promises-in-depth
You basically want to wait for all of them to finish before resolving getSurveysToUpload, yes? In that case, you can return $q.all() in your getSurveysToUpload().then()
For example (not guaranteed working code, but you should get an idea):
surveyDataLayer.getSurveysToUpload().then(function(surveys) {
var promises = [];
// This type of loop will not work in older IEs, if that's of any consideration to you
for (var item in surveys) {
var survey = surveys[item];
promises.push(ajaxserviceAPI.postSurvey(survey));
}
var allPromise = $q.all(promises)
.then(function(responses) {
// Again, we want to wait for the completion of all setLocalSurveyServerId calls
var promises = [];
for (var index = 0; index < responses.length; index++) {
var response = responses[index];
promises.push(surveyDataLayer.setLocalSurveyServerId(survey, response.result));
}
return $q.all(promises);
});
return allPromise;
}).then(function() {
alert('Done'); // This never gets run
});
With promise API, how to send two asynchronous request in parallel, and resolve the combined result as the response.
var get = function(id){
var res1, res2;
var deferred = $q.defer();
Db.get(id, "abc")
.then(function (d) {
//deferred.resolve(d));
res1 = d;
}, function (e) {
//error
});
Db.get(id, "def")
.then(function (d) {
//deferred.resolve(d));
res2 = d;
}, function (e) {
//error
});
//?????? how to return {res1:res1 , res2: res2}
return deferred.promise;
};
now, when I call get() like
get(123).then(function(d)){
// d= {res1: res1, res2: res2}
},
...
I need to get the combined result as indicated. How to do this with Angular promise API?
As #Matt said, you need to use $q.all, but the usage isn't quite right. AngularJS doesn't support .done and .fail and they don't work quite like that anyway in that there's no such thing as a promise for multiple values, instead you just have a promise for an array.
If you were writing this using the full Q we would write:
var get = function (id) {
return Q.all([Db.get(id, "abc"), Db.get(id, "def")])
.spread(function (res1, res2) {
return {res1: res1, res2: res2};
});//the error case is handled automatically
};
In this case, .spread acts like .then except that it spreads the results of an array for a promise out over the arguments of its onFulfilled function. To change this to use the promise methods from AngularJS we just need to make do without .spread. This leads to the following solution:
var get = function (id) {
return $q.all([Db.get(id, "abc"), Db.get(id, "def")])
.then(function (res) {
return {res1: res[0], res2: res[1]};
});//the error case is handled automatically
};
The beauty of this is that we are freed from handling all the nitty grity of error propagation and storing the partial results because .then acts as a filter. If you leave out the error handler, it automatically propagates any errors. This means that if either of the input promises are rejected, the result will be rejected. If both promises are fulfilled successfully, res is the array of those resolution values.
I have something to add to #ForbesLindesay answer.
In our case, we wanted partial results: if a request failed (eg. server has an hiccup, we request something deleted by somebody else, etc.), we still want to collect the valid responses, and to report the errors.
I found out that we need to handle success and failure on each promise, returning a value that will be collected by $q.all.
Here is our code, simplified and made generic ('item'...):
var promiseList = _.map(itemList, function(item)
{
return DataService.getISubtems(item.id)
.then(
function(response)
{
var subItems = response.data;
$log.info('Received sub-item list;' + subItems.length + ';items received');
return subItems;
},
function(reason)
{
$log.warn('Sub-item list not received for item;' + item.name + ';' + item.id);
$scope.errorList.push('Sub-item list not received for item "' + item.name + '"');
}
);
});
$q.all(promiseList)
.then(function(itemArray)
{
// We get an array of arrays interleaved with undefined value when an error was raised.
// That's because error handling doesn't return anything, ie. returns undefined.
// We remove these undefined values then put all operations at the same level.
var allOperations = _(operationArray).reject(_.isUndefined).flatten().value();
if ($scope.errorList.length > 0)
{
NotificationService.warning('Items Fetching', 'Errors while getting item list:\n' +
$scope.errorList.join('\n'));
}
$scope._onItemListReceived(allItems);
});
Note that we use Lodash (_.map, _.flatten, _.reject, _.isUndefined) but I think the usage is quite clear (that's the nice point of this library!).
You can use the angular-q-spread library and then use the same code as #ForbesLindesay's first example:
// The module needs $q-spread as a dependency:
// angular.module('…', ['$q-spread']);
var get = function (id) {
return $q.all([Db.get(id, "abc"), Db.get(id, "def")])
.spread(function (res1, res2) {
return {res1: res1, res2: res2};
});
};