There are at least three issues here:
- Timing issue with an asynchronous callback.
- Your
for
loop index is not valid inside the asynchronous callback.
- Your error handling is not going to work because doing a
throw
from inside an asynchronous callback does not do anything useful.
Here's more detail on each issue.
Your primary problem is this is a timing issue. connection.query()
is asynchronous. That means that it does not block and it finishes some time in the future. So, you call connection.query()
and other code continues to run. In fact, your entire for
loop runs starting all the connection.query()
calls and then, some time later, the callback is called for each connection.query()
.
Thus, if you look for the value of usN
in your response.render()
call, it does not yet have a value because none of the connection.query()
operations have yet finished and thus the callbacks have not yet been called. The only place you can reliably use the usN
value is inside of your connection.query()
callback. Since you're doing that multiple times in for
loop, you will have to keep track of when ALL the callbacks are done.
There are many different ways to solve this, but here's one where you keep a counter of how many of the async callbacks in your for
loop have completed and you call the render when they have all been called.
In addition, because the callback is called later, your for
loop index i
is no longer valid inside the callback either. That can be solved by wrapping it all in an IIFE that captures the loop counter separately for each callback.
And, trying to if(err2) throw err2;
inside the callback will not do anything useful because doing a throw
in this type of async callback just throws into the database code and will not be something you can catch anywhere in your own code. Instead, you will have to communicate an error back via some sort of your own callback. Promises are actually a much better way to communicate and propagate async errors. Here's an implementation with the first two items fixed (the error handling is not corrected here as that requires some other structural changes):
function(err, rows, fields){
if(err) throw err;
if(rows.length > 0){
for (var i = 0; i < rows.length; i++) {
exp[i] = "Explanation: " + rows[i].explanation + ' ';
}
var usN = [];
var cnt = 0;
for (var i = 0; i < rows.length; i++) {
(function(index) {
connection.query("select * from users where id =" + rows[i].userID, function(err2, rows2, fields2){
// ISSUE: doing a throw here does nothing useful
// as it just goes back into the async database code
// You need a better way to propagate errors
if(err2) throw err2;
if(rows2.length > 0){
usN[index] = "Edited by: " + rows2[0].username;
}
// see if this is the last callback
++cnt;
if (cnt === rows.length) {
response.render("gloss.jade", { user: request.session.user, logedIn: request.session.user, yourWord: word, exp: exp, users: usN});
}
});
})(i);
}
}
}