Ist*_*med 8 asynchronous node.js express node-mysql
在我的node.js,表达应用程序,我正在与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)
});
Run Code Online (Sandbox Code Playgroud)
我尝试使用异步中间件,但徒劳无功,因为我无法弄清楚在这种情况下使用哪个函数.
简而言之,要求是:
1)第一个回调中的所有异步事物都应该以同步方式完成.
2)只有在完成所有计算之后才应该将响应发送到ajax调用,而不是在之前(如果事情是异步的,因为它们在现有代码中可能会发生,不是吗?)
Mic*_*ley 16
它可能只是语义,但重要的是要理解你不能以同步的方式运行它.您必须异步运行它,并管理处理的顺序以获得所需的效果.我发现在我想要转换数据(例如函数式编程)方面而不是在更加同步的环境中编写的命令式代码时,更多地考虑这些问题是有用的.
从代码中我可以看出,您希望最终得到一个数据结构,super_cats
如下所示:
[
{
super_id: 1,
cats: [
{
cat_id: 2,
cat_name: "Category",
subcats: [
{
subcat_id: 3,
subcat_name: "Subcategory"
},
...
]
},
...
]
},
...
]
Run Code Online (Sandbox Code Playgroud)
让我们首先将它解压缩为一个带有单个回调的函数调用.
function getCategoryTree(callback) {
}
Run Code Online (Sandbox Code Playgroud)
现在,让我们从顶部开始吧.您希望运行单个异步函数(SQL查询),并且希望生成每个结果一个条目的数组.这对我来说听起来像是一个map
操作.但是,因为我们希望(其中一个值cats
)以异步方式确定,我们需要使用异步映射,它的async
库提供.
我们现在就填写async.map
签名吧; 我们希望映射在我们results
(这是我们的功能相当于for
循环),并为每一个我们希望把结果变成什么 -the异步函数,它的东西就是所谓的迭代器.最后,一旦我们拥有了所有变换后的数组元素,我们就想调用赋给函数的回调.
function getCategoryTree(callback) {
conn.query("SELECT * FROM `super_cats`", function(error, results, fields) {
async.map(results, iterator, callback);
});
}
Run Code Online (Sandbox Code Playgroud)
让我们创建一个新函数来获取顶级类别信息,并使用其名称代替我们的iterator
占位符.
function getCategoryTree(callback) {
conn.query("SELECT * FROM `super_cats`", function(error, results, fields) {
async.map(results, getSuperCategory, callback);
});
}
function getSuperCategory(resultRow, callback) {
}
Run Code Online (Sandbox Code Playgroud)
现在我们需要决定我们想要为每个人回馈什么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 });
});
});
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,一旦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 });
});
});
}
Run Code Online (Sandbox Code Playgroud)
现在,我们只需要实施getSubCategory
.
function getSubCategory(resultItem, callback) {
callback(null, {
subcat_id: resultItem.id,
subcat_name: resultItem.subcategory
});
}
Run Code Online (Sandbox Code Playgroud)
哎呀!我们需要的数据getSubCategory
没有异步组件!事实证明,我们根本不需要那个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
};
}
Run Code Online (Sandbox Code Playgroud)
值得注意的是,我们的原始方法运行良好; 如果有机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, " "));
});
Run Code Online (Sandbox Code Playgroud)
这里有一些效率低下的问题,但为了简单起见,我已经掩饰了它们.例如,不是一遍又一遍地运行第二个子查询,而是可以立即查询所有类别ID,然后一次查询所有类别,等等.然后,一旦获得所有数据,就可以遍历每个同步阵列以拉出你需要的部分.
此外,有更好的方法在关系数据库中存储树结构; 特别是,看一下Modified Preorder Tree Traversal.