Nodejs/Javascript 提升,变量保持未定义

Nodejs/Javascript hoisting, variable remains undefind

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});
        }
    }
)

usN数组取值未定义,exp数组取值未未定义。我可以请你帮我解决这个问题吗?

这里至少存在三个问题:

  1. 异步回调的计时问题。
  2. 您的 for 循环索引在异步回调中无效。
  3. 您的错误处理不会起作用,因为从异步回调内部执行 throw 不会做任何有用的事情。

这里有关于每个问题的更多详细信息。

你的主要问题是这是一个时间问题。 connection.query() 是异步的。这意味着它不会阻塞并且会在未来某个时间完成。因此,您调用 connection.query(),其他代码继续到 运行。事实上,您的整个 for 循环 运行 开始所有 connection.query() 调用,然后,一段时间后,为每个 connection.query().

调用回调

因此,如果您在 response.render() 调用中查找 usN 的值,它还没有值,因为 connection.query() 操作中的 none尚未完成,因此尚未调用回调。唯一可以可靠地使用 usN 值的地方是在 connection.query() 回调中。由于您在 for 循环中多次执行此操作,因此您必须跟踪所有回调何时完成。

有许多不同的方法可以解决这个问题,但这里有一个方法,您可以在其中记录 for 循环中有多少异步回调已完成,并在它们全部被调用时调用渲染.

此外,由于稍后调用回调,您的 for 循环索引 i 在回调内也不再有效。这可以通过将其全部包装在一个 IIFE 中来解决,该 IIFE 为每个回调分别捕获循环计数器。

而且,在回调中尝试 if(err2) throw err2; 不会做任何有用的事情,因为在这种类型的异步回调中做 throw 只会抛出数据库代码,而不是你可以捕捉到的东西在您自己的代码中的任何位置。相反,您将不得不通过某种您自己的回调来传达错误。 Promises 实际上是一种更好的交流和传播异步错误的方式。这是修复了前两项的实现(此处未更正错误处理,因为这需要进行一些其他结构更改):

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);             
        }
    }
}