I'm working on a function (called by an express.js route) to merge event info in a database with its Facebook counterpart and return it as an array of event objects.
I am having trouble with the asynchronous nature of node.js and resolving a variable number of promises within a foreach loop before returning the whole object. I've tried numerous different methods of rearranging my code (callbacks, counters, promises, etc.), but I have not been successful in solving this problem, and I would really like to know why. I suspect it has to do with variables being overwritten in the foreach loop, but I'm not sure how to solve that.
I am looking for three things:
- What am I not grasping conceptually that is needed to solve this problem?
- How would I figure this out or debug this in the future?
- How do I fix my code to make it work?
Here is my function:
function mergeEvents(req, res, next, events){
console.log("Merge Events");
var dfd = q.defer();
ensureAuthenticated(req, res, next).then(function(auth){
var iEvent, event;
var promises = [];
if (auth){
console.log("authenticated!");
console.log("auth token: " + ACCESS_TOKEN);
for (iEvent in events){
event = events[iEvent];
var promise = q.defer();
promises.push(promise);
https.get('https://graph.facebook.com/' + event.fb_id + '?access_token=' + ACCESS_TOKEN, function(response) {
var str = '';
response.on('data', function(chunk){
str += chunk;
});
response.on('end', function(){
var fb_event = JSON.parse(str);
event.dataValues.fb = fb_event;
promise.resolve(event);
});
});
if (promises.length == events.length){
console.log("last run through");
q.all(promises).then(function(results){
console.log("all promises completed?");
console.log(results[0]); //OUTPUT BELOW
//more code in here... but promises haven't resolved
//...
dfd.resolve(events);
});
}
}
}else{
console.log("Not authenticated. Redirecting to main page.");
dfd.resolve(events);
}
});
return dfd.promise;
}
While I am trying to get a JSON object, it returns an unresolved promise on console.log(results[0]):
{ promise: [object Object],
resolve: [Function],
fulfill: [Function],
reject: [Function],
notify: [Function] }
Code references I have viewed:
- https://github.com/kriskowal/q
- https://github.com/kriskowal/q/wiki/API-Reference
- https://strongloop.com/strongblog/how-to-compose-node-js-promises-with-q/
- http://thejsguy.com/javascript/node.js/2014/06/27/JavaScript-Flow-Control.html
Oh, and here's my function for a single event fb/db merge that works, so you can compare:
function mergeEvent(req, res, next, event){
console.log("Merge Event");
var dfd = q.defer();
ensureAuthenticated(req, res, next).then(function(auth){
if (auth){
console.log("authenticated!");
console.log("auth token: " + ACCESS_TOKEN);
https.get('https://graph.facebook.com/' + event.fb_id + '?access_token=' + ACCESS_TOKEN, function(response) {
var str = '';
response.on('data', function(chunk){
str += chunk;
});
response.on('end', function(){
var fb_event = JSON.parse(str);
event.dataValues.fb = fb_event;
dfd.resolve(event);
});
});
}else{
console.log("not authenticated. redirecting to main page");
dfd.resolve(event);
}
});
return dfd.promise;
}