javascript - How to resolve a variable number of promises in node.js -
i'm working on function (called express.js route) merge event info in database facebook counterpart , return array of event objects.
i having trouble asynchronous nature of node.js , resolving variable number of promises within foreach loop before returning whole object. i've tried numerous different methods of rearranging code (callbacks, counters, promises, etc.), have not been successful in solving problem, , know why. suspect has variables being overwritten in foreach loop, i'm not sure how solve that.
i looking 3 things:
- what not grasping conceptually needed solve problem?
- how figure out or debug in future?
- how fix code make work?
here 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); (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... promises haven't resolved //... dfd.resolve(events); }); } } }else{ console.log("not authenticated. redirecting main page."); dfd.resolve(events); } }); return dfd.promise; }
while trying json object, returns unresolved promise on console.log(results[0]):
{ promise: [object object], resolve: [function], fulfill: [function], reject: [function], notify: [function] }
code references 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, , here's function single event fb/db merge works, 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 main page"); dfd.resolve(event); } }); return dfd.promise; }
your main problem here:
var promise = q.defer(); promises.push(promise);
q.defer()
not return promise. returns deferred.
var result = q.defer(); promises.push(result.promise);
naming variables correctly important, didn't see mistake because chose improper variable names.
that being said...
- avoid
for .. in
. arrays have.foreach()
or.map()
. - instead of checking
if (promises.length == events.length)
, move part out of loop. - your function pretty long , use little refactoring.
- and of course, don't call deferred objects "deferred" or promise objects "promise". that's not descriptive.
- read through what explicit promise construction antipattern , how avoid it? (let sink in, takes time)
here's use.
var q = require('q'); var qhttp = require("q-io/http"); // -> https://github.com/kriskowal/q-io var fb = { // collect other fb api methods here, maybe transform module graph: function (id) { var url = 'https://graph.facebook.com/' + id + '?access_token=' + access_token; return qhttp.read(url).then(function (data) { return json.parse(data.tostring()); }); } }; function mergeevents(req, res, next, events) { return ensureauthenticated(req, res, next).then(function (auth) { if (!auth) return q.reject("not authenticated."); return q.all(events.map(function (event) { return fb.graph(event.fb_id).then(function (data) { event.datavalues.fb = data; return event; }); }).then(function (results) { //more code in here... })); }); }
note: if wrote ensureauthenticated
, modify reject directly , on own instead of resolving falsy auth
value need check every time use it. line if (!auth) ...
removed after that.
also, //more code in here...
part deals "enhanced" events should probably live outside of mergeevents
.