当前位置: 首页 > 面试题库 >

node.js,表达-以同步方式在循环内一次又一次执行mysql查询

楚皓君
2023-03-14
问题内容

在我的node.js
Express应用程序中,我正在使用superagent中间件进行ajax调用。该调用使用Node-
mysql
中间件通过许多数据库查询来获取复杂数组中的数据库数据。

在粘贴代码之前,我试图用语言解释我要做什么,尽管代码足以说明它想要做什么,另外, 第一个回调 中的
所有异步操作都应该以同步方式完成。

说明:

在第一个查询的回调中,将执行一次for循环以多次运行第二个查询,并且在每个循环之后,仅在第二个查询的回调完成后才调用下一个循环。接下来的代码行也是如此。

码:

但是, 如果需要,您可以 跳过 for循环的内幕(在注释中标记),以使事情简短明了。

conn.query("SELECT * FROM `super_cats`",function(error, results, fields) {

        if(error){console.log("erro while fetching products for homepage "+ error);}

        for(var i in results) { // FIRST FOR LOOP INSIDE THE FIRST QUERY CALLBACK

            /*Innards of for loop starts*/    
            var elem = new Object();
            var supcat_id=results[i].id;
            elem.super_id =supcat_id;
            elem.cats=new Array();
            var cat= '';
            /*Innards of for loop ends*/

            conn.query("SELECT * FROM `categories` WHERE `supcat_id`="+supcat_id,function(error_cats, results_cats, fields_cats) {

                if (error_cats) {console.log("erro while fetching cats for menu " + error_cats);}

                for(var j in results_cats) {

                    /*Innards of for loop starts*/    
                    cat= new Object();
                    var cat_id=results_cats[j].id;
                    cat.cat_id=cat_id;
                    cat.cat_name=results_cats[j].cat_name;
                    cat.subcats=new Array();    
                    /*Innards of for loop starts*/

                    conn.query("SELECT * FROM `subcategories` WHERE `category`="+cat_id,function(error_subcats, results_subcats, fields_subcats) { 
                        if (error_subcats) {console.log("erro while fetching subcats for menu " + error_subcats);}   
                        for(var k in results_subcats ){

                            /*Innards of for loop starts*/    
                            var subcat=new Object();
                            var subcat_id=results_subcats[k].id;
                            subcat.subcat_id=subcat_id;
                            subcat.subcat_name=results_subcats[k].subcategory;
                            cat.subcats.push(subcat);
                            elem.cats.push(cat);    
                            /*Innards of for loop starts*/

                        }// end of for loop for  results_subcats

                    });

                }// end of for loop for result_cats
            });

            super_cats.push(elem);    
         }// end of for supercat results

    res.send(super_cats)    
});

我尝试了异步中间件,但徒劳无功,因为我无法弄清楚在这种情况下要使用哪个功能。

简而言之,要求是:

1)第一个回调中的所有异步操作都应以同步方式完成。

2)仅在完成所有计算之后才应将响应发送到ajax调用,而不是在此之前发送给ajax调用(因为如果事物与现有代码中的事物是异步的,可能会发生,
不是吗?


问题答案:

它可能只是语义,但重要的是要了解您 不能
以同步方式运行它。您必须异步运行它,并管理处理顺序才能获得所需的效果。我发现,从我想如何转换数据(功能编程)的角度考虑这些问题是有用的,而不是在更加同步的环境中编写命令性代码。

从代码可以看出,您最终想要一个super_cats看起来像这样的数据结构:

[
  {
    super_id: 1,
    cats: [
      {
        cat_id: 2,
        cat_name: "Category",
        subcats: [
          {
            subcat_id: 3,
            subcat_name: "Subcategory"
          },
          ...
        ]
      },
      ...
    ]
  },
  ...
]

首先,将其提取到具有单个回调的单个函数调用中。

function getCategoryTree(callback) {
}

现在,让我们从顶部开始。您要运行一个异步函数(SQL查询),并且要生成一个数组,每个结果只有一个条目。对我来说,这听起来像是一项map手术。但是,因为我们希望(其中一个值cats)以异步方式确定,我们需要使用异步映射,它的async库提供。

现在让我们填写async.map签名。我们要映射到我们resultsfor循环(这是我们循环的功能等效项),并且对于每个循环,我们都希望将结果转换为
某种东西 -执行该 的异步函数称为迭代器。最后,一旦我们拥有所有转换后的数组元素,我们就想调用赋予函数的回调。

function getCategoryTree(callback) {
  conn.query("SELECT * FROM `super_cats`", function(error, results, fields) {
    async.map(results, iterator, callback);
  });
}

让我们创建一个用于获取顶级类别信息的新函数,并使用其名称代替iterator占位符。

function getCategoryTree(callback) {
  conn.query("SELECT * FROM `super_cats`", function(error, results, fields) {
    async.map(results, getSuperCategory, callback);
  });
}

function getSuperCategory(resultRow, callback) {
}

现在我们需要确定我们要为每个项目退还什么resultRow。根据上面的图表,我们想要一个对象,该对象super_id等于行的ID,并且cats等于顶级类别中的所有类别。但是,由于cats也是异步确定的,因此我们需要运行下一个查询并转换
这些 结果,然后才能继续。

与上次类似,我们希望cats数组中的每个项目都是一个对象,该对象具有来自查询结果的一些信息,但是我们还想要一个subcats数组,该数组又是异步确定的,因此我们将async.map再次使用它。但是,这一次,我们将使用匿名函数进行回调,因为在将结果提供给更高级别的回调之前,我们想对结果做一些事情。

function getSuperCategory(resultItem, callback) {
  var supcat_id = resultItem.id;

  conn.query("SELECT * FROM `categories` WHERE supcat_id` = " + supcat_id, function(error, results, fields) {
    async.map(results, getCategory, function(err, categories) {
      callback(err, { super_id: supcat_id, cats: categories });
    });
  });
}

如您所见,一旦async.map完成,就意味着我们在此超级类别下拥有所有类别。因此,我们可以调用callback我们想要在数组中的对象。

至此,我们只需要实现即可getCategory。它将看起来非常类似于getSuperCategory,因为我们想要做基本上相同的事情-
对于每个结果,返回一个对象,该对象具有查询中的一些数据,但也具有异步组件。

function getCategory(resultItem, callback) {
  var cat_id = resultItem.id;
  var cat_name = resultItem.cat_name;

  conn.query("SELECT * FROM `subcategories` WHERE `category` = " + cat_id, function(error, results, fields) {
    async.map(results, getSubCategory, function(err, subcategories) {
      callback(err, { cat_id: cat_id, cat_name: cat_name, subcats: subcategories });
    });
  });
}

现在,我们只需要实现即可getSubCategory

function getSubCategory(resultItem, callback) {
  callback(null, {
    subcat_id: resultItem.id,
    subcat_name: resultItem.subcategory
  });
}

糟糕!我们需要的数据getSubCategory没有异步组件!事实证明,我们根本不需要最后一个async.map。我们本可以使用常规的数组映射;让我们改变getCategorygetSubCategory以这种方式工作。

function getCategory(resultItem, callback) {
  var cat_id = resultItem.id;
  var cat_name = resultItem.cat_name;

  conn.query("SELECT * FROM `subcategories` WHERE `category` = " + cat_id, function(error, results, fields) {
    var subcategories = results.map(getSubCategory);
    callback(error, { cat_id: cat_id, cat_name: cat_name, subcats: subcategories });
  });
}

function getSubCategory(resultItem) {
  return {
    subcat_id: resultItem.id,
    subcat_name: resultItem.subcategory
  };
}

值得注意的是,我们原来的方法效果很好;如果有可能getSubCategory具有异步组件,则可以将其保持原样。

就是这样!这是我编写此答案时编写的代码;请注意,我不得不伪造一下SQL,但我认为这个想法就存在了:

var async = require("async");

// fake out sql queries
queryNum = 0;
var conn = {
  query: function(query, callback) {
    queryNum++;
    var results = [1, 2, 3, 4, 5].map(function(elem) {
      return {
        id: queryNum + "-" + elem,
        cat_name: "catname-" + queryNum + "-" + elem,
        subcategory: "subcategory-" + queryNum + "-" + elem
      };
    });
    callback(null, results, null);
  }
};

function getCategoryTree(callback) {
  conn.query("SELECT * FROM `super_cats`", function(error, results, fields) {
    async.map(results, getSuperCategory, callback);
  });
}

function getSuperCategory(resultItem, callback) {
  var supcat_id = resultItem.id;

  conn.query("SELECT * FROM `categories` WHERE supcat_id` = " + supcat_id, function(error, results, fields) {
    async.map(results, getCategory, function(err, categories) {
      callback(err, { super_id: supcat_id, cats: categories });
    });
  });
}

function getCategory(resultItem, callback) {
  var cat_id = resultItem.id;
  var cat_name = resultItem.cat_name;

  conn.query("SELECT * FROM `subcategories` WHERE `category` = " + cat_id, function(error, results, fields) {
    var subcategories = results.map(getSubCategory);
    callback(error, { cat_id: cat_id, cat_name: cat_name, subcats: subcategories });
  });
}

function getSubCategory(resultItem) {
  return {
    subcat_id: resultItem.id,
    subcat_name: resultItem.subcategory
  };
}

getCategoryTree(function(err, result) {
  console.log(JSON.stringify(result, null, "  "));
});

这里存在一些效率低下的问题,但是为了简单起见,我已经将它们掩盖了。例如,您可以一次查询所有类别ID,然后一次查询所有类别,而不是一遍又一遍地运行第二个子查询,然后,一旦获得所有数据,就可以循环遍历每个子查询。同步排列以取出所需的零件。

另外,还有更好的方法将树结构存储在关系数据库中。特别是,请看一下修改后的预排序树遍历。



 类似资料:
  • 问题内容: 我很难弄清楚为什么while循环实际上不会循环。它运行一次并停止。 我正在尝试使其循环,以便用户能够多次转换单位。任何帮助都欢迎! 问题答案: 问题在于,当您呼叫时,它会占用该号码,但不会占用该号码之后的换行符。要解决此问题,只需在调用后放一行代码。 示例和完整说明: 假设您输入“ km”,按回车,“ 123”,按回车。从程序的角度来看,输入流为。 该代码获取值,并且使输入超出第一个。

  • 问题内容: 我只是发现我认为PLSQL与Oracle中的SQL有点意外的行为。 如果我在SQLDeveloper上运行此查询,则会得到5个结果: 但是,如果我在SQLDeveloper中运行以下语句: 变量 w_counter 以值1(怪异)结束 但最奇怪的部分是,如果我将查询封装在子查询中…… 该 w_counter 变量完成与价值5 … 你对此有什么要说的? 我正在使用Oracle 9.2i

  • 问题内容: 上面的语句返回3行。但是下面的语句仅返回2行。 我知道为什么会这样,但是有一种方法可以强制item_id 1返回两次? 我要退货的示例: id-> 1筹码€2.50 id-> 1筹码€2.50 id-> 2可口可乐€1.60 -------------------- 总计€6.60 问题答案: 您可以加入另一个表,例如 或者只是在您的应用程序中复制它们。 您实际上不需要做您所要的。

  • 我试图用Java实现一个简单的客户机-服务器应用程序。 这是代码: 客户端.java 服务器.java 这是主要类: 代码的逻辑很简单:客户端和服务器都在等待< code>while(true)循环中的消息。 服务器的< code>listen方法中的< code>while循环执行得很好。但是,在< code>listenForMessages方法中,循环似乎只执行一次。我只看到一个“在循环”印

  • 我正在使用mysql存储过程,我花了两个小时试图弄清楚为什么这个游标只运行一次。(我假设它只运行了一次,因为我运行这个存储过程后只看到一条记录——最后有一个命令,我从前面创建的临时表中选择了所有内容) 注意,我已经尝试过在游标中单独运行查询,它运行正常(返回了多条记录,这是应该的)。 我认为这一定与我从循环中插入数据有关(我希望在每个循环过程中向临时表中插入一行数据)。

  • 我对python中双for循环的使用感到困惑,这是我的代码: 输出如下: 它只对外循环的第一个值执行内循环,为什么会发生这种情况?我怎样才能让它在第一个和第二个变量的所有组合上循环?