Reputation: 544
I'm having a bit of trouble with an itterative function in nodejs.
I'm stepping through an object and checking if that object has any sub-objects attached (think: a star has a planet has a moon has an orbital station has a ship).
I'm trying to assemble this all into a nice array of objects to push to the client.
Here's the function:
var subNodeProc = function(nodeList,sqlP,itteration_count) {
var async = require('async');
--itteration_count;
async.each(nodeList,function(dd,cb){
var simple = {
sql:sqlP,
values:[dd.node_id],
timeout:40000
};
dd.subnodes = false;
connection.query(simple, function(err,rslt){
if (err) {
cb(err);
} else {
if (rslt.length > 0) {
var r = nodeList.indexOf(dd);
if (itteration_count > 0) {
rslt = subNodeProc(rslt,sqlP,itteration_count);
}
nodeList[r].subnodes = rslt;
}
cb();
}
});
},function(err){
if (err) {
return err;
} else {
return nodeList;
}
});
}
When I trigger the function it returns a nodelist of undefined. Can anyone give me a pointer in the right direction? I can't get it to work
Thanks!
Edit: here's a sample of the data I'm itterating over:
The SQL statement:
SELECT n.id as node_id, n.name, n.system_id, n.parent_id as parent_id FROM nodes as n WHERE n.parent_id = ?
Sample nodeList for input:
[ { node_id: 1,
name: 'Planet A',
system_id: 1,
parent_id: null,
},
{ node_id: 2,
name: 'Moon',
system_id: 1,
parent_id: 1,
},
{ node_id: 3,
name: 'Debris',
system_id: 1,
parent_id: 2,
},
{ node_id: 4,
name: 'Asteroid',
system_id: 1,
parent_id: 1,
} ]
Moon A has a parent_id of 1 and node_id of 2, moon A also has a ship (ship A, node_id:3, parent_id:2) orbiting it.
What I want :
[ { node_id: 1,
name: 'Planet A',
system_id: 1,
parent_id: null,
subnodes:[{
node_id: 2,
name: 'Moon A',
system_id: 1,
parent_id: 1,
subnodes: [{
node_id:3,
name: 'Ship A',
system_id:1,
parent_id:2
},
{...}]
},
{...}]
},
{...}]
Upvotes: 0
Views: 83
Reputation: 544
Okay, the solution was pretty obvious, once I sussed it out. Much thanks to @shennan for getting me going.
The key is:
and
Here's what I've come up with. Hope someone can look it over and give me an opinion...
// Main processing function.
var subNodeProc = function(nodeList,sqlP,itteration_count,cback) {
var async = require('async');
itteration_count--;
async.each(nodeList,function(dd,cb){
if (itteration_count > 0) {
// Trigger SQL Walker subNodeProcWalker with the necessary data (dd.node_id in this case, with a callback)
subNodeProcWalker(dd.node_id,sqlP,itteration_count,function(nl) {
// Hey look! the walker has done its business. Time to fill the subnode and tell async we're done with this array node.
dd.subnodes = nl;
cb();
});
}
},function(){
// At the end of the run, return the nodelist intact.
cback(nodeList);
});
}
// SQL Walker with callback for subNodeProc
var subNodeProcWalker = function(node_id,sqlP,itteration_count,cback){
// assemble the object for the query and do the query
var simple = {
sql:sqlP,
values:[node_id],
timeout:40000
};
connection.query(simple, function(err,rslt){
if (err) {
console.log('Error in Query');
console.log(simple);
console.log(err);
cback(false);
} else {
// no error and a result? Quick! Trigger subNodeProc again
if (rslt.length > 0) {
subNodeProc(rslt,sqlP,itteration_count,function(nodePol) {
// Lookie lookie! A result from subNodeProc! There's life there! Quick! tell this function we're done!
cback(nodePol);
});
} else {
cback(false);
}
}
});
}
Upvotes: 0
Reputation: 11656
It's hard to tell whether there are any other major issues because I cannot see the data which you are feeding the method. However, there is one major problem with this: you're attempting to return
data from a method that uses asynchronous method calls.
The asynchronous way is to return values via a callback. In your code, the very last function
in your example (the callback) is called from a completely different scope (from within the async
framework) so your nodeList
or err
is being lost in a scope you don't control.
You need to rethink your code so that the returned data is passed to a callback. You could leverage the async
callback for this. Ad a callback argument to your subNodeProc
method. Then you can call that callback, after async
has finished, passing it the nodeList
:
var subNodeProc = function (nodeList, sqlP, itteration_count, cb) {
var async = require('async');
--itteration_count;
async.each(nodeList,function(dd, cb){
var simple = {
sql:sqlP,
values:[dd.node_id],
timeout:40000
};
dd.subnodes = false;
connection.query(simple, function(err, rslt){
if (err) {
cb(err);
} else {
if (rslt.length > 0) {
var r = nodeList.indexOf(dd);
if (itteration_count > 0) {
rslt = subNodeProc(rslt,sqlP,itteration_count);
}
nodeList[r].subnodes = rslt;
}
cb();
}
});
}, function (err) {
if (err)
throw err;
else
cb(nodeList);
});
}
You would then use the method like this:
subNodeProc(nodeList, sqlP, itteration_count, function (processed) {
console.log(processed);
/* do whatever you want afterwards here */
});
Upvotes: 1