Using Async with Recursion in Node to Return All Results
I have a database where parent records can have unlimited levels of children. The following simplified records illustrate:
id: 0, parent: null
id: 1, parent: 0
id: 2, parent: 0
id: 3, parent: 2
I have been trying for weeks now to write a recursive function in node/express that generates a javascript object representing this tree of records and NOT returning until it is complete. I can do this no problem as long as I include a timeout, but clearly that is sub-optimal.
This is my most recent attempt:
var getChildren = function (parent, callback) {
const query = "SELECT ID, ParentID, NULL AS Children FROM myTable WHERE "ParentID = '" + parent.ID + "'";
db.doQuery (query, function (err, results) {
if(err) {
callback (true);
} else {
if (results.length == 0) callback (false, parent); // We didn't find any children so we end the recursion
parent.Children = results; // Each time we get a result set we need to add that set to our Children property
// Iterate through our result set to find the next layer of children
var i = 0;
async.whilst (
function () { return i < results.length },
function (callback) {
i++;
getChildren(parent.Children[i - 1], function (err, results) {
if (err) callback (true);
})
},
callback (false, parent)
);
}
})
}
In short this is a recursive routine that reads all children as records from a database. It adds this result set to the object property 'Children' and then it then iterates through the result set and for each one recursively calls itself to find more children of that record. The JSON that is ultimately returned looks like this:
{
"ID": "000000000",
"ParentID": null,
"Children": [
{
"ID": "000000001",
"ParentID": "000000000",
"Children": null
},
{
"ID": "000000002",
"ParentID": "000000000",
"Children": [
{
"ID": "000000003",
"ParentID": "000000002",
"Children": null
}
]
}
]
}
As you can probably already tell, what actually happens is the code above returns only the first level of children until I put a timeout in the calling function. I have tried several async functions, I have tried counting timeouts, I've tried re-writing for loops with their own recursive functions with callback, and I cannot for the life of me get this to work. I know that the problem is that I just can't wrap my head around some async magic that will help me identify when the actual recursion is completely finished and callback to the calling function.
Is this a case where I have to keep track of the levels of recursion I have called and then count them down as they callback before I use a final callback?
Any insight would be greatly appreciated.
asynchronous recursion callback
add a comment |
I have a database where parent records can have unlimited levels of children. The following simplified records illustrate:
id: 0, parent: null
id: 1, parent: 0
id: 2, parent: 0
id: 3, parent: 2
I have been trying for weeks now to write a recursive function in node/express that generates a javascript object representing this tree of records and NOT returning until it is complete. I can do this no problem as long as I include a timeout, but clearly that is sub-optimal.
This is my most recent attempt:
var getChildren = function (parent, callback) {
const query = "SELECT ID, ParentID, NULL AS Children FROM myTable WHERE "ParentID = '" + parent.ID + "'";
db.doQuery (query, function (err, results) {
if(err) {
callback (true);
} else {
if (results.length == 0) callback (false, parent); // We didn't find any children so we end the recursion
parent.Children = results; // Each time we get a result set we need to add that set to our Children property
// Iterate through our result set to find the next layer of children
var i = 0;
async.whilst (
function () { return i < results.length },
function (callback) {
i++;
getChildren(parent.Children[i - 1], function (err, results) {
if (err) callback (true);
})
},
callback (false, parent)
);
}
})
}
In short this is a recursive routine that reads all children as records from a database. It adds this result set to the object property 'Children' and then it then iterates through the result set and for each one recursively calls itself to find more children of that record. The JSON that is ultimately returned looks like this:
{
"ID": "000000000",
"ParentID": null,
"Children": [
{
"ID": "000000001",
"ParentID": "000000000",
"Children": null
},
{
"ID": "000000002",
"ParentID": "000000000",
"Children": [
{
"ID": "000000003",
"ParentID": "000000002",
"Children": null
}
]
}
]
}
As you can probably already tell, what actually happens is the code above returns only the first level of children until I put a timeout in the calling function. I have tried several async functions, I have tried counting timeouts, I've tried re-writing for loops with their own recursive functions with callback, and I cannot for the life of me get this to work. I know that the problem is that I just can't wrap my head around some async magic that will help me identify when the actual recursion is completely finished and callback to the calling function.
Is this a case where I have to keep track of the levels of recursion I have called and then count them down as they callback before I use a final callback?
Any insight would be greatly appreciated.
asynchronous recursion callback
See thetraverse
example at the bottom of this answer
– user633183
Nov 16 '18 at 6:35
add a comment |
I have a database where parent records can have unlimited levels of children. The following simplified records illustrate:
id: 0, parent: null
id: 1, parent: 0
id: 2, parent: 0
id: 3, parent: 2
I have been trying for weeks now to write a recursive function in node/express that generates a javascript object representing this tree of records and NOT returning until it is complete. I can do this no problem as long as I include a timeout, but clearly that is sub-optimal.
This is my most recent attempt:
var getChildren = function (parent, callback) {
const query = "SELECT ID, ParentID, NULL AS Children FROM myTable WHERE "ParentID = '" + parent.ID + "'";
db.doQuery (query, function (err, results) {
if(err) {
callback (true);
} else {
if (results.length == 0) callback (false, parent); // We didn't find any children so we end the recursion
parent.Children = results; // Each time we get a result set we need to add that set to our Children property
// Iterate through our result set to find the next layer of children
var i = 0;
async.whilst (
function () { return i < results.length },
function (callback) {
i++;
getChildren(parent.Children[i - 1], function (err, results) {
if (err) callback (true);
})
},
callback (false, parent)
);
}
})
}
In short this is a recursive routine that reads all children as records from a database. It adds this result set to the object property 'Children' and then it then iterates through the result set and for each one recursively calls itself to find more children of that record. The JSON that is ultimately returned looks like this:
{
"ID": "000000000",
"ParentID": null,
"Children": [
{
"ID": "000000001",
"ParentID": "000000000",
"Children": null
},
{
"ID": "000000002",
"ParentID": "000000000",
"Children": [
{
"ID": "000000003",
"ParentID": "000000002",
"Children": null
}
]
}
]
}
As you can probably already tell, what actually happens is the code above returns only the first level of children until I put a timeout in the calling function. I have tried several async functions, I have tried counting timeouts, I've tried re-writing for loops with their own recursive functions with callback, and I cannot for the life of me get this to work. I know that the problem is that I just can't wrap my head around some async magic that will help me identify when the actual recursion is completely finished and callback to the calling function.
Is this a case where I have to keep track of the levels of recursion I have called and then count them down as they callback before I use a final callback?
Any insight would be greatly appreciated.
asynchronous recursion callback
I have a database where parent records can have unlimited levels of children. The following simplified records illustrate:
id: 0, parent: null
id: 1, parent: 0
id: 2, parent: 0
id: 3, parent: 2
I have been trying for weeks now to write a recursive function in node/express that generates a javascript object representing this tree of records and NOT returning until it is complete. I can do this no problem as long as I include a timeout, but clearly that is sub-optimal.
This is my most recent attempt:
var getChildren = function (parent, callback) {
const query = "SELECT ID, ParentID, NULL AS Children FROM myTable WHERE "ParentID = '" + parent.ID + "'";
db.doQuery (query, function (err, results) {
if(err) {
callback (true);
} else {
if (results.length == 0) callback (false, parent); // We didn't find any children so we end the recursion
parent.Children = results; // Each time we get a result set we need to add that set to our Children property
// Iterate through our result set to find the next layer of children
var i = 0;
async.whilst (
function () { return i < results.length },
function (callback) {
i++;
getChildren(parent.Children[i - 1], function (err, results) {
if (err) callback (true);
})
},
callback (false, parent)
);
}
})
}
In short this is a recursive routine that reads all children as records from a database. It adds this result set to the object property 'Children' and then it then iterates through the result set and for each one recursively calls itself to find more children of that record. The JSON that is ultimately returned looks like this:
{
"ID": "000000000",
"ParentID": null,
"Children": [
{
"ID": "000000001",
"ParentID": "000000000",
"Children": null
},
{
"ID": "000000002",
"ParentID": "000000000",
"Children": [
{
"ID": "000000003",
"ParentID": "000000002",
"Children": null
}
]
}
]
}
As you can probably already tell, what actually happens is the code above returns only the first level of children until I put a timeout in the calling function. I have tried several async functions, I have tried counting timeouts, I've tried re-writing for loops with their own recursive functions with callback, and I cannot for the life of me get this to work. I know that the problem is that I just can't wrap my head around some async magic that will help me identify when the actual recursion is completely finished and callback to the calling function.
Is this a case where I have to keep track of the levels of recursion I have called and then count them down as they callback before I use a final callback?
Any insight would be greatly appreciated.
asynchronous recursion callback
asynchronous recursion callback
asked Nov 16 '18 at 3:20
srcsrc
1
1
See thetraverse
example at the bottom of this answer
– user633183
Nov 16 '18 at 6:35
add a comment |
See thetraverse
example at the bottom of this answer
– user633183
Nov 16 '18 at 6:35
See the
traverse
example at the bottom of this answer– user633183
Nov 16 '18 at 6:35
See the
traverse
example at the bottom of this answer– user633183
Nov 16 '18 at 6:35
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53330942%2fusing-async-with-recursion-in-node-to-return-all-results%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53330942%2fusing-async-with-recursion-in-node-to-return-all-results%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
See the
traverse
example at the bottom of this answer– user633183
Nov 16 '18 at 6:35