它可能只是语义,但重要的是要了解您 不能
以同步方式运行它。您必须异步运行它,并管理处理顺序才能获得所需的效果。我发现,从我想如何转换数据(功能编程)的角度考虑这些问题是有用的,而不是在更加同步的环境中编写命令性代码。
从代码可以看出,您最终想要一个
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 queriesqueryNum = 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,然后一次查询所有类别,而不是一遍又一遍地运行第二个子查询,然后,一旦获得所有数据,就可以循环遍历每个子查询。同步排列以取出所需的零件。
另外,还有更好的方法将树结构存储在关系数据库中。特别是,请看一下修改后的预排序树遍历。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)