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
; 我们可以使用一个规则的数组映射; 让我们改变getCategory
和getSubCategory
以这种方式工作。
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同步拉出你需要的部分。
此外,还有更好的方法来存储关系数据库中的树结构; 特别是,看看修改先序树遍历。
- 从数据库获取最新的5个数据
- 以asynchronous方式执行Express res.render
- connect.session的秘密和connect.cookieParser的区别?
- Node.js,Express,MongoDB和stream
- 处理path如/ search /:search(/ filter1 /:filter1)?
- expression错误中间件不处理从promise.done()抛出的错误
- Node.js,Express,jQuery.param() – 从JSON中删除引号
- 我应该在node.js和Express中链接方法和函数吗?
- 关于express.cookieSession()的几个问题