【问题标题】:Node & MySQL - connection.query inside connection.query - Object property not accessible节点和 MySQL - connection.query 内部 connection.query - 对象属性不可访问
【发布时间】:2016-04-07 14:37:50
【问题描述】:

我遇到了一个非常尴尬的问题。 我创建一个池,连接到数据库,创建一个连接和查询,获取结果,做很多事情,然后我必须创建另一个连接和查询,但实际上它必须是动态的,所以我循环遍历我的数组 @ 987654321@ 包含数据。

然后发生了更多代码,我必须创建一个额外的循环,因为我的 teacherHours 数组的某些元素必须多次尝试才能从即将到来的查询中获得正确的响应。

接下来是另一个循环,它应该循环到availableHours > 0。现在,一切都在这里了。

大量代码发生在第二个循环中,我准备了第二个查询,调用 connection.query() 并在回调函数内部准备了第三个查询(在做了一些其他事情之后),这实际上是 Node 启动的地方我出去。

它给了我TypeError: Cannot read property 'tid' of undefined。我的第三个查询需要访问tid,所以我尝试像以前一样访问它,但 Node 不允许它。

我知道查询返回有用的数据(行),因此不会是查询但没有接收到数据的问题。实际上我console.log("the rowRIDS"+rowRIDS); 是第二个查询的结果,我看到它返回 2 行,然后它给了我错误。

对我来说也很奇怪的是,我的两个循环中的所有 console.logs 都被记录了,而我的第二个查询(包含 2 行)的 console.log 在循环运行后被记录了,因为如果返回的 2 行和错误出现在循环的第一次迭代中,查询是嵌套的,因为此时代码应该访问第二个查询。

顺便说一句,我尝试设置硬编码数字而不是 tid 只是为了让下一个属性 datum 出错。我有种感觉,好像变量teacherHours 超出了范围,但它应该是一个全局变量。

为了更好地理解我在说什么,我复制了代码并取消了所有 javascript 代码的注释,我在其中填充和计算内容。任何帮助都会非常棒,几乎 7 小时的尝试和错误都没有任何运气。谢谢!

 pool.getConnection(function(err, connection){
  if (err) throw err;

  connection.query('SELECT * FROM teachers_teaching_tbl WHERE fwdid = 1 ', function(err, rows, fields) {  
    if (err) {
      console.error('error querying: ' + err.stack);
      return;
    } 
    rowArray=rows;
    console.log(rowArray);
    // 
    // HERE HAPPENS
    // A LOOOOT OF STUFF
    // 
    // AND teacherHours IS BEING POPULATED
    // 
    // THEN COMES A FOR LOOP 
    for(var i=0; i<teacherHours.length;i++){
      //
      // MORE STUFF
      //
      //AND ANOTHER LOOP
      while(availableHours>0){//do{ ORIGINALLY I TRIED WITH A DO WHILE LOOP
        //
        // AGAIN A BUNCH OF STUFF
        //
        // NOW I'M PREPARING MY NEXT QUERY 
        // 
        var myQueryGetFreeRoom=" SELECT rms.rid FROM rooms_tbl as rms WHERE NOT EXISTS ( "; 
        myQueryGetFreeRoom+=" SELECT NULL FROM classes_tbl as cls  "; 
        myQueryGetFreeRoom+="  WHERE ( (cls.bis > '"+bisMinus1+"' AND cls.bis <= '"+realBis+"' ) OR ( cls.von > '"+bisMinus1+"' AND cls.von < '"+realBis+"' ) ) AND (cls.datum = '"+teacherHours[i].datum.getFullYear()+"-"+(teacherHours[i].datum.getMonth()+1)+"-"+teacherHours[i].datum.getDate()+"') AND (cls.rid=rms.rid)  )  ";
        //
        //
        connection.query(myQueryGetFreeRoom, function(err, rowRIDS, fields) { 
          if (err) {
            console.error('error querying: ' + err.stack);
            return;
          }
          roomIDs=rowRIDS;
          console.log("the rowRIDS"+rowRIDS);
          //
          // MORE STUFF 
          // HAPPENING
          //
          if(roomIDs.length>0){
            //
            // PREPARING QUERY NO.3 - WHICH IS WHERE MY ERROR POINTS - TO THE USE OF tid PROPERTY
            //
            var myQueryBookClass = " INSERT INTO classes_tbl ( rid , tid , belegtAnz, datum, von , bis )  ";
            myQueryBookClass+="  VALUES ( "+Math.floor(Math.random() * roomIDs.length)+", "+teacherHours[i].tid+" , 0, '"+teacherHours[i].datum.getFullYear()+"-"+(teacherHours[i].datum.getMonth()+1)+"-"+teacherHours[i].datum.getDate()+"' , '"+bisMinus1+"' , '"+realBis+"' ) ";
            console.log("myQueryBookClass: "+myQueryBookClass);
            availableHours = 0;
            //
            // HERE WAS SUPPOSED TO FOLLOW QUERY 3 - myQueryBookClass 
            // 
            // BUT SINCE I DONT EVEN GET INSIDE HERE IT IS IN COMMENTS
            // 
            /*connection.query(myQueryBookClass, function(err, insertRows, fields){
              if(err){
                console.error('error querying: '+err.stack);
                return;
                }
              console.log("Inserted Rows: "+ insertRows);
            }); */

          } else { 
            availableHours= availableHours - 1;
            //
            // STUFF HAPPENING
            //
          } 
        });
        availableHours= availableHours - 1;  
      }//while(availableHours>0); 
    //
    }

    connection.release(function(err){
              if (err){
                console.error('error disconnecting: ' + err.stack);
                return;
              }
    });
  });
});

【问题讨论】:

  • 顺便说一句。最初我不想在第二个connection.query() 之后使用availableHours= availableHours - 1; ,而只是想在第二个connection.query() 中使用它。但如果我这样做,我会得到一个无限循环,第二个 connection.query() 永远不会被输入.. 奇怪

标签: javascript mysql node.js


【解决方案1】:

我认为您来自 Python、Java 等非异步语言,这就是为什么 Node(即 JavaScript)似乎会为您搞砸,但实际上并非如此。

您的代码中存在的问题是,您在同一个 while 循环中同时同步执行像 query 这样的异步函数。您需要使用像 async 这样的模块,它有助于异步运行和收集结果。

这是更新后的代码。

var async = require('async'),
    connection;

async.waterfall([
    function (cb) {
        pool.getConnection(cb);
    },
    function (conn, cb) {
        connection = conn;

        connection.query('SELECT * FROM teachers_teaching_tbl WHERE fwdid = 1', cb);
    },
    function (rows, fields, cb) {
        rowArray = rows;
        console.log(rowArray);

        // HERE HAPPENS
        // A LOOOOT OF STUFF
        // 
        // AND teacherHours IS BEING POPULATED
        // 
        // THEN COMES A FOR LOOP
        async.eachSeries(teacherHours, function (teacherHour, done) {
          // MORE STUFF
          //
          //AND ANOTHER LOOP
          async.whilst(function () {
            return availableHours > 0;
          }, function (cb) {
            // AGAIN A BUNCH OF STUFF
            //
            // NOW I'M PREPARING MY NEXT QUERY 
            // 
            var myQueryGetFreeRoom =
                "SELECT rms.rid FROM rooms_tbl AS rms WHERE NOT EXISTS ("
                    + "SELECT NULL FROM classes_tbl AS cls"
                    + " WHERE ("
                        + "(cls.bis > '" + bisMinus1 + "' AND cls.bis <= '" + realBis + "')"
                        + " OR (cls.von > '" + bisMinus1 + "' AND cls.von < '" + realBis + "')"
                    + ") AND ("
                        + "cls.datum = '" + teacherHour.datum.getFullYear() + "-" + (teacherHour.datum.getMonth() + 1) + "-" + teacherHour.datum.getDate() + "'"
                    + ") AND cls.rid = rms.rid";

            async.waterfall([
                function (cb) {
                    connection.query(myQueryGetFreeRoom, cb);
                },
                function(rowRIDS, fields, cb) {
                    roomIDs = rowRIDS;
                    console.log("the rowRIDS" + rowRIDS);
                    //
                    // MORE STUFF 
                    // HAPPENING
                    //
                    if (roomIDs.length > 0) {
                        //
                        // PREPARING QUERY NO.3 - WHICH IS WHERE MY ERROR POINTS - TO THE USE OF tid PROPERTY
                        //
                        var myQueryBookClass = "INSERT INTO classes_tbl (rid, tid, belegtAnz, datum, von, bis) VALUES ("
                                + Math.floor(Math.random() * roomIDs.length) 
                                + ", " + teacherHour.tid 
                                + ", 0, '" + teacherHour.datum.getFullYear() + "-" + (teacherHour.datum.getMonth() + 1) + "-" + teacherHour.datum.getDate() + "', '" + bisMinus1 + "', '" + realBis + "')";

                        console.log("myQueryBookClass: " + myQueryBookClass);

                        availableHours = 0;
                        //
                        // HERE WAS SUPPOSED TO FOLLOW QUERY 3 - myQueryBookClass 
                        // 
                        // BUT SINCE I DONT EVEN GET INSIDE HERE IT IS IN COMMENTS
                        // 
                        connection.query(myQueryBookClass, function (err, insertRows, fields) {
                            if (err) {
                                console.error('error querying: '+err.stack);
                                return;
                            }

                            console.log("Inserted Rows: "+ insertRows);
                            // Here do whatever you need to do, then call the callback;
                            cb();
                        }); 
                    } else {
                        --availableHours;
                        //
                        // STUFF HAPPENING
                        //
                        cb();
                    } 
                }
            ], function (err) {
                if (!err) {
                    // Notice that you are decrementing the `availableHours` twice here and above.
                    // Make sure this is what you want.
                    --availableHours;
                } 

                cb(err);
            });
          }, done);
        }, function (err) {
            connection.release(function (err) {
              if (err) {
                console.error('error disconnecting: ' + err.stack);
                return;
              }
            });
        });
    }
], function (err) {
    conn && pool.release(conn);

    err && throw err;
});

下次请正确格式化您的代码以获得更好的可读性,这将有助于您更快地获得答案,并将您的问题文本分成几段以达到相同的目的。

说明

有四个嵌套的async 流:

async.waterfall

  -> async.eachSeries

     -> async.whilst

        -> async.waterfall
  1. 基本上,async.waterfall 库允许您执行一系列函数。

    • 在前一个函数返回响应后才会执行每个下一个函数。
    • 为了表明一个函数已经完成并且结果可用,它必须调用回调,在我们的例子中是cb(你可以随意调用它,例如callback)。规则是调用它,否则下一个函数将永远不会执行,因为上一个函数似乎还没有完成它的工作。
    • 上一个函数完成后,它会调用提供的cb,签名如下:

      cb(err, connection);
      
    • 如果请求连接时出错,整个async.waterfall会中断,最后的回调函数会被执行。

    • 如果没有错误,连接将作为第二个参数提供。 async 模块将前一个函数的所有参数作为第一个、第二个等参数传递给下一个函数,这就是为什么第二个函数接收 conn 作为第一个参数的原因。
    • 每个下一个函数都会收到回调 cb 作为最后一个参数,您最终必须在工作完成时调用它。

    • 因此,在第一个async.waterfall 流中:

      1. 它请求一个新的数据库连接。
      2. 一旦连接可用,就会执行下一个函数,将查询发送到数据库。
      3. 等待查询结果,一旦结果可用,就可以运行下一个迭代每一行的函数。
  2. async.eachSeries 允许按顺序迭代给定的值数组。

    • 在第二个async.eachSeries流中:

      1. 它依次迭代teacherHours 数组中的每个元素。
      2. 处理完每个元素后(无论您想要什么),您都必须调用done 回调。同样,您可以将其称为cb,就像之前的async.waterfallcallback 一样。 done 只是为了清楚说明该过程已完成
  3. 然后我们有async.whilst,它提供与普通while () {} 语句相同的逻辑,但异步处理循环。

    • 在这第三个async.whilst 流中:

      1. 调用第一个函数。它的返回值指示它是否必须继续循环,即调用第二个异步函数。
      2. 如果返回值真实 (availableHours &gt; 0),则调用第二个函数。
      3. 当异步函数完成后,它必须调用提供的回调cb 来表示它已经结束。然后async 模块将调用第一个函数来检查它是否必须继续循环。
  4. async.whilst 内部的这个异步函数中,我们还有另一个async.waterfall,因为您需要为每个teacherHour 向数据库发送查询。

    • 在这最后的第四个async.watercall 流:

      1. 它将SELECT 查询发送到数据库。
      2. 等待响应。一旦rowRIDS 可用,它就会调用waterfall 中的第二个函数。
      3. 如果有rowRIDSroomIDs.length &gt; 0),则发送INSERT查询到数据库。
      4. 完成后,它会调用回调cb
      5. 如果没有rowRIDs,它也会调用回调cb,以指示工作已完成。

JavaScript 是异步的,这是一件很棒的事情。从其他同步语言转换一开始可能会很困难,但是一旦你明白了,同步就很难了。它变得如此直观,以至于您会开始思考为什么其他语言不能异步工作。

我希望我能彻底解释上面的代码。享受 JavaScript!太棒了!

【讨论】:

  • 感谢您的帮助和意见。你是对的,我来自 C 和 Java。我尝试使用您的代码(安装了异步包并应用了更改),但 Node 没有启动。 err &amp;&amp; throw err; ^^^^^ SyntaxError: Unexpected token throw 把我踢出去了,你介意重新检查你的脚本吗?顺便提一句。当它起作用时,如果你能解释一些事情会很好,因为有些事情我不太明白,即使我阅读了异步包的示例。
  • 我将最后一行 err &amp;&amp; throw err; 包裹到了这个 if (err) throw err; 中(不确定是否相同),但之后 Node 确实启动了。可悲的是,前一个外部 for 循环的迭代,现在 async.eachSeries() 不会继续迭代。我只在屏幕上看到了 teacherHours 的第一个元素。我在整个 javascript 中创建了日志,以查看发生了什么。在第三个查询myQueryBookClass 之后,我也做了一个。我知道我的teacherHours 数组的长度为 7,所以我应该看到 7 次迭代。
  • 我明白了!抱歉,没有在if(roomIDs.length &gt; 0){…} 中调用cb(); 是我愚蠢的做法。我不想立即插入数据库,而是想稍微研究一下代码,所以我评论了connection.query(){…}); 和下面的cb(); - 不知道它会停止迭代。所以在插入cb(); 之后一切正常。现在我也做了我的插入。我真的非常感谢你的帮助。你介意向我解释一下这些调用背后的逻辑吗?
  • 解释了答案中的代码。不要忘记 UP 答案。
  • 非常感谢!解释太棒了。我认为我的声誉很低,无法投票给答案,但它仍然很棒。谢谢
猜你喜欢
  • 2017-09-19
  • 2021-09-14
  • 2019-02-10
  • 2021-02-20
  • 1970-01-01
  • 2020-01-20
  • 2019-01-06
  • 2021-04-18
  • 2012-03-27
相关资源
最近更新 更多