I have this code:
var services = ['EC2', 'S3', 'RDS', 'IAM']
var promises = [];
for(var i = 0; i < services.length; i++) {
var promise = awsStatus('us-east-1', services[i])
promises.push(promise);
console.log(promises);
}
q.all(promises).then(function(data){
console.log(promises);
});
it's supposed to loop on the services array with the awsStatus method. The problem is that sometimes I get all the results I want:
{ service: 'IAM', status: 0 }
{ service: 'EC2', status: 0 }
{ service: 'RDS', status: 0 }
But sometimes I get incomplete results. I thought I needed .then after awsStatus but that also didn't resolve this. What else is wrong in this piece of code?
since now there's a comment, I've also tried this :
var services = ['EC2', 'S3', 'RDS', 'IAM']
var promises = [];
for(var i = 0; i < services.length; i++) {
var promise = awsStatus('us-east-1', services[i]).then(function(promise){
promises.push(promise);
//console.log(promises);
});
}
q.all(promises).then(function(data){
console.log(promises);
});
and it produces the same results.
Try using .each
Promise.each(services, function(service) {
return awsStatus('us-east-1', service).then(function(promise){
promises.push(promise);
//console.log(promises);
});
}).then(function() {
console.log('Done with all instances');
});
I can suggest a solution using async library as follows:
var services = ['EC2', 'S3', 'RDS', 'IAM']
async.map(services,
function(item, done){
awsStatus('us-east-1', item).then(
function(){
done(null, "Completed item " + item);
},
function(err){
done(err + " in item " + item, null);
}
);
},
function(err, results){
if(err) return console.log(err);
console.log(results);
}
);
It will fail and stop executing if one of the promises returns an error. If dont want to stop you can return null in the error function of promise instead of error message as the first argument of done method
There are two problems in your code:
Using q.all() does not guarantee that the code will wait for all the promises to resolve. If one of them is rejected, the then method would be called, not waiting for the rest of the promises. You can solve that by using q.allResolved() instead.
You are logging the wrong objects. You should be logging the results of the promises, and not the promise objects themselves.
Example of how to solve both problems:
q.allResolved(promises).then(function(results) {
console.log(results);
}).catch(function(err) {
console.error(err);
});
Related
I'm working with Node.js on async calls to noSQL DynamoDB. I first query to see which 'buddy list(s)' the account belongs. That may return from zero to 'n' new Primary Keys that will contain lists of all of the members of each of those buddy lists. Think of them as clubs to which a person belongs... you may have none or many; each club has several members or even one.
So far (and I am working with Promises for the first time here... though I have used callbacks on prior JA projects) I'm OK with where I am, but I know that I am assembling the array of promises incorrectly. That is, I can see in the console that the .then function executes before both promises resolve. For the record, I do expect that... it seems reasonable that any .then may be satisfied with a single promise resolving.
Anyways, Can someone offer up some tips as to how to structure? I'd like to end up with pseudo:
getBuddyLists
.then(getAllTheBuddiesInTheLists)
.then(function(){//doSomething});
my Node.js code:
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({region:'us-east-1'});
var buddyGroups = [];
exports.handler = function(event, context, callback) {
var params = {
TableName: 'USER_INFORMATION',
Key: {
"DEVICE_ID": event.DEVICE_ID
}
};
var getBuddyList = new Promise(function(resolve, reject){
docClient.get(params, function(err, data){
if(err){
reject(err);
}
else{
var dataJSON = JSON.parse(JSON.stringify(data));
dataJSON.Item.my_buddies.values.forEach(function(value){
buddyGroups.push(value);
});
console.log('Got buddy groups: ' + buddyGroups);
resolve(buddyGroups);
}
});
});
getBuddyList.then(function(capturedData){ // capturedData => an array of primary keys in another noSQL document
console.log('groups: ' + capturedData);
var myPromises = [];
capturedData.forEach(function(value){
var reqParams = {
TableName: "buddy_list",
Key: {"BUDDY_ID": value}
};
myPromises.push(new Promise (function(resolve, reject){
docClient.get(reqParams, function(err, data){
if(err){
//console.log(err, data);
reject(err);
}else{
var returnedJSON = JSON.parse(JSON.stringify(data));
console.log(returnedJSON);
resolve(returnedJSON);
}
});
}));
});
//
Promise.all(myPromises); // ADDED IN EDIT <<<<<<<<<<<<<<<<<<<<<<
// how to make sure all of myPromises are resolved?
//
}).then(function(){
console.log("done");
})
.catch(function(err){
console.log("error message:" + error);
});
};
EDIT: Added location of Promise.all(myPromises);
Just to clarify how you would use Promise.all, even though you accepted an answer, you edited the question, but still are not using it correctly
Also, the discussion of .map vs .forEach - this is how you would use .map
getBuddyList.then(function(capturedData){ // capturedData => an array of primary keys in another noSQL document
console.log('groups: ' + capturedData);
// changes to use .map rather than .forEach
// also, you need to RETURN a promise - your edited code did not
return Promise.all(capturedData.map(function(value){
return new Promise (function(resolve, reject){
var reqParams = {
TableName: "buddy_list",
Key: {"BUDDY_ID": value}
};
docClient.get(reqParams, function(err, data){
if(err){
//console.log(err, data);
reject(err);
}else{
var returnedJSON = JSON.parse(JSON.stringify(data));
console.log(returnedJSON);
resolve(returnedJSON);
}
});
});
}));
}).then(function(){
console.log("done");
})
.catch(function(err){
console.log("error message:" + error);
});
You're looking for Promise.all(myPromises), which returns a single promise of an array of the results of the promises.
I'm working with Nodejs and i want to use promises in order to make a full response after a for loop.
exports.getAlerts = function(req,res,next){
var detected_beacons = [];
if (!req.body || Object.keys(req.body).length == 0) {
res.status(401);
res.json({message:'No data sent'});
return
}
var nets = req.body.networks;
db.collection("beaconConfig", function(err, beaconConfigCollection){
if (!err){
var promises = [];
for(var i=0;i<nets.length;i++){
var defer = q.defer();
beaconConfigCollection.find({$or: [{"data.major" : nets[i].toString()},{"data.major" : nets[i]}], batteryLevel : {$lt : 70}}).toArray(function(errFind, saver){
if (!errFind && saver && saver.length > 0){
promises.push(defer.promise);
console.log("--------------------savers -------------------");
console.log(saver);
for(var j=0; j<saver.length;j++){
console.log("--------------------saver[j]-------------------");
console.log(saver[j]);
var detected = {}
var major = saver[j].data.major;
detected.major = major;
console.log("--------------------detected -------------------");
console.log(detected);
detected_beacons.push(detected);
defer.resolve(detected);
}
}
});
}
q.all(promises).then(function(results){
console.log("--------------------detected_beacons -------------------");
console.log(detected_beacons);
res.json(detected_beacons);
});
} else {
console.error(err);
res.status(500);
res.json({message:"Couldn't connect to database"});
}
});};
All the consoles.log works fine unless the last one, the ---detected_beacons--- one, which is THE FIRST ONE to be shown and it is empty.
That is the reason why i'm thinking that the promises are not working well. I have var q = require('q'); at the top and the mongo connection does not return any problem.
Thanks for the help.
First of all, an awesome guide about how to get along with Promises.
Well, haters gonna hate but there is nothing wrong with Promises at all (at least, I hope so).
According to 'MongoDb for Node' documentation, .toArray() returns a Promise, like most of the methods of this library. I felt free to make some appointments along your code:
exports.getAlerts = function(req, res, next) {
if (!req.body || Object.keys(req.body).length == 0) {
res.status(401);
res.json({message: 'No data sent'});
return;
}
// db.collection returns a promise :)
return db.collection("beaconConfig").then(function(beaconConfigCollection) {
// you can use .map() function to put together all promise from .find()
var promises = req.body.networks.map(function(net) {
// .find() also returns a promise. this promise will be concat with all
// promises from each net element.
return beaconConfigCollection.find({
$or: [{
"data.major": net.toString()
}, {
"data.major": net
}],
batteryLevel: {
$lt: 70
}
}).toArray().then(function(saver) {
// you can use the .find() response to create an array
// with only the data that you want.
return saver.map(function(saverElement) {
// your result array will be composed using saverElement.data.major
return saverElement.data.major;
});
}).catch(function(err) {});
});
// use q.all to create a promise that will be resolved when all promises
// from the array `promises` were resolved.
return q.all(promises);
}).then(function(results) {
console.log("results", results);
}).catch(function(err) {});
};
I hope it helps you!
UPDATE - Solution:
I was for looping instead should have used MongoDBs $in feature - details here
I have the following Q.all block:
Q.all([
getFirstBlock(),
getSecondBlock(),
])
.then(function(){
getSecondBlockProcessed();
})
.then(function(){
res.json(completeArray);
});
The problem I have is when I go into the final then block I notice that the function getSecondBlockProcessed has not been completed.
Everything in the first Q.all is done.
Why isn't that promise getting resolved?
And the method in question looks like:
var getSecondBlockProcessed = function() {
return Q.promise(function(resolve, reject) {
for (var i=0; i<mostInRegion.length; i++){
Person.find(
{_id: mostInRegion[i]['_id']},
{question:1, country: 1},
function(err, found) {
mostInRegion2.push(found);
})
}
resolve();
});
}
Any help would be appreciated/what have i overlooked?
Thanks
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 want to update multiple rows in a class in Parse. I need to add a new field using "set". I tried saveAll and Promises in parallel to update, but these both are asynchronous. So they consume lot of resources and bandwidth.
How can I do that in a synchronous way. It would be better for me if you can answer using promises in series
Here is the code I'm using currently.But I need in a series way
Parse.Cloud.define("Updating",function(request,response){
var query = new Parse.Query("FollowUp");
query.find({
success: function(results){
for (var i = 0; i < results.length; i++) {
results[i].set("testfield","sometext");
}
Parse.Object.saveAll(results,{
success: function(list){
response.success("ok");
},
error: function(error){
response.error("failed");
}
});
},
error: function(error) {}
});
});
This code is working fine and it is synchronous.
Parse.Cloud.define("Updating",function(request,response){
var query = new Parse.Query("FollowUp");
query.find().then(function(results) {
var promise = Parse.Promise.as();
_.each(results, function(result) {
// For each item, extend the promise with a function to save it.
result.set("newfield","somevalue");
promise = promise.then(function() {
// Return a promise that will be resolved when the save is finished.
return result.save();
});
});
return promise;
}).then(function() {
response.success("working!!");
// Every object is updated.
});
});
or you can even use "for" loop instead of _.each
Parse.Cloud.define("Updating",function(request,response){
var query = new Parse.Query("FollowUp");
query.find().then(function(results) {
var promise = Parse.Promise.as();
for(var i=0;i<results.length;i++){
results[i].set("newfield","somevalue");
promise = promise.then(function() {
// Return a promise that will be resolved when the save is finished.
return results[i].save();
});
});
return promise;
}).then(function() {
response.success("working!!");
// Every object is updated.
});
});