0
var exp = [];   
connection.query(`select * 
                  from glossary 
                  where ${connection.escape(word)} = word`, 
    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 = []; 
            for (var i = 0; i < rows.length; i++) {  
                connection.query("select * from users where id =" + rows[i].userID, function(err2, rows2, fields2){                                            
                    if(err2) throw err2;
                    if(rows2.length > 0){                                 
                        usN[i] = "Edited by: " + rows2[0].username; 
                    }
                });                       
            }
            response.render("gloss.jade", { user: request.session.user, logedIn: request.session.user, yourWord: word, exp: exp, users: usN});
        }
    }
)

The usN array gets the value but remains undefind, the exp array gets the value and it does not reman undefind. May i ask you to help me out with this.

fredmaggiowski
  • 2,232
  • 3
  • 25
  • 44
  • This has nothing to do with hoisting. Not even with scope. (or, well, maybe a [tiny little bit](http://stackoverflow.com/q/750486/1048572)) – Bergi Apr 11 '16 at 03:03
  • @Bergi - That duplicate does not actually advise the OP how to solve their specific problem because they need to know when N callbacks are done. Yes, it covers the general topic, but doesn't offer a specific solution to this problem. – jfriend00 Apr 11 '16 at 05:58
  • @Bergi - And, this code also has a `for` loop index problem which is also not described in the dup. – jfriend00 Apr 11 '16 at 06:08

1 Answers1

1

There are at least three issues here:

  1. Timing issue with an asynchronous callback.
  2. Your for loop index is not valid inside the asynchronous callback.
  3. 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);             
        }
    }
}
jfriend00
  • 683,504
  • 96
  • 985
  • 979