在我的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
签名。我们想要映射到我们results
的for
循环上(这是循环的功能等效项),并且对于每个循环,我们都希望将结果转换为某种东西-执行该某事的异步函数称为迭代器。最后,一旦我们拥有所有转换后的数组元素,我们就想调用赋予函数的回调。
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
。我们本可以使用常规的数组映射;让我们改变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
具有异步组件,则可以将其保持原样。
就是这样!这是我编写此答案时编写的代码;请注意,我不得不伪造一点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,然后一次查询所有类别,而不是一遍又一遍地运行第二个子查询,然后,一旦获得所有数据,就可以循环遍历每个子类别。同步排列以取出所需的零件。
此外,还有更好的方法将树结构存储在关系数据库中。特别是,请看一下修改后的预排序树遍历。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句