node.js,以同步的方式在循环中一个接一个地执行mysql查询

在我的node.js中,表示应用程序,我用superagent中间件做了一个ajax调用。 该调用通过相当多的数据库查询使用node-mysql中间件获取复杂数组中的数据库数据。

在粘贴代码之前,我试图用言语解释我正在尝试做什么,尽pipe代码足以说明在第一个callback中的所有asynchronous事件应该以同步的方式完成。

说明:

在第一个查询的callback中,执行for循环多次运行第二个查询,在每个循环之后,只有在第二个查询的callback完成后才会调用下一个循环。 下一个代码行也是一样的。

码:

但是 ,如果你愿意的话,你可以跳过 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) }); 

我尝试了asynchronous中间件,但徒劳无功,因为我不知道在这种情况下使用哪个函数。

简而言之,要求是:

1)第一个callback中的所有asynchronous事件应该以同步方式完成。

2)只有在所有的计算完成之后,响应才会被发送到ajax调用中(而不是在之前的代码中, 如果它们是asynchronous的,可能会发生这种情况, 不是吗?

这可能只是语义上的,但重要的是要明白,你不能以同步的方式运行这个。 您必须asynchronous运行它,并pipe理处理的顺序以获得所需的效果。 我觉得在如何转换数据(function性编程)方面更多考虑这些问题是有用的,而不是我在更为同步的环境中编写的命令性代码。

从代码中我可以知道,你想要在super_cats中的数据结构看起来像这样:

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

我们先从一个简单的callback中抽取出一个函数调用。

 function getCategoryTree(callback) { } 

那么,现在,我们从顶端开始。 你想运行一个单一的asynchronous函数(一个SQL查询),并且你想要为每个结果生成一个数组。 对我来说这听起来像是一个map操作 。 但是,因为我们希望asynchronous地确定其中一个值( cats ),所以我们需要使用asynchronous映射,这是async库提供的 。

我们现在只需填写async.map签名; 我们想要映射我们的results (这是我们的for循环的function等价物),并且对于每个结果我们都想把结果变成一些东西 – 做这些事情的asynchronous函数被称为迭代器。 最后,一旦我们拥有了所有我们已经转换的数组元素,我们想调用给我们的函数的callback函数。

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

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

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

现在我们需要决定我们想要给每个resultRow返回resultRow 。 根据上面的图表,我们想要一个super_id等于行ID的对象,而super_id等于顶级类别中的所有类别。 但是,由于cats也是asynchronous确定的,我们需要运行下一个查询并转换这些结果,然后才能继续。

与上一次类似,我们希望subcats数组中的每个项目都是一个包含来自查询结果的一些信息的对象,但是我们也需要一个subcats数组, subcats是asynchronous确定的,所以我们将再次使用async.map 。 但是这一次,我们将使用一个匿名函数来进行callback,因为我们希望在结果给予更高级别的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 }); }); }); } 

正如你所看到的,一旦这个async.map完成,这意味着我们有这个超类别下的所有类别; 因此,我们可以调用我们想要在数组中的对象的callback

现在完成了,我们只需要实现getCategory 。 它看起来与getSuperCategory非常相似,因为我们希望做基本相同的事情 – 对于每个结果,返回一个包含查询中一些数据的对象,但也是一个asynchronous组件。

 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中获取的数据没有asynchronous组件! 事实certificate,我们根本不需要最后一个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有一个asynchronous组件,你可以保持原样。

而就是这样! 这是我写这个答案时写的代码。 请注意,我不得不伪造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,然后一次查询所有的类别等等。然后,一旦你有所有的数据,你可以遍历每个arrays同步拉出你需要的部分。

此外,还有更好的方法来存储关系数据库中的树结构; 特别是,看看修改先序树遍历。