javascript:异步递归调用驱使我疯狂
问题描述:
我有一个分层系统。一个类别有一个parentid(如果它是根类别,则为0)。javascript:异步递归调用驱使我疯狂
例排在一个类别的DB:
name | id | parentid
-----------+------+----------
Label | 71 | 8
我需要得到特定类别的所有子类别。不仅意味着一个类别的直接子女,而且意味着所有子女的每一个子女。
我已经做过递归的东西,但是在同步环境中,它让我想起它并不完全一样。
我知道我离工作解决方案并不遥远,但它并不适用于所有情况。注意:删除所有调试日志行以避免混淆问题。
此外,欢迎任何简化和/或优化。例如,我不喜欢有两个回调函数,一个是递归函数,最后一个回调函数......(但也许是因为它需要这样的异步)。
整个事情应该返回给定类别的所有子类别的ID的数组(allCats
)。
这个当前的解决方案已经适用于没有孩子的单个类别,并且一个等级下降(并且allCats
正确包含所有ID)。在两个级别失败(最终回调从未被调用,所以cnt
未被正确更新?)。
搜索是通过调用Category.getAllSubCategories(categoryId);
Category.getSubCategories = function(cat, cnt, fullCats, cb, finalCb) {
Category.find({where: {parentId: cat.id}}, function(err, cats) {
if (err) {
cb(err);
}
if (cats.length > 0) {
var ids = [];
for (var i=0; i<cats.length; i++) {
ids.push(cats[i].id);
}
fullCats = fullCats.concat(ids);
cb(null, cnt, fullCats, cats, finalCb);
} else {
if (cnt > 0) cnt -= 1;
cb(null, cnt, fullCats, null, finalCb);
}
});
}
var catSearchCallback = function(err, cnt, fullCats, cats, finalCb) {
if (err) {
finalCb(err);
}
if (cats) {
for (var c=0; c<cats.length; c++) {
cnt += 1;
Category.getSubCategories(cats[c], cnt, fullCats, catSearchCallback, finalCb);
}
} else {
if (cnt == 0) {
finalCb(null, fullCats);
}
}
}
/* start here */
Category.getAllSubCategories = function(categoryId, cb) {
Category.findById(categoryId, function(err, cat) {
if (err) {
return logger.error(err);
}
var fullCats = []; //collection holding ALL ids
var cnt = 0; //counter to count how many steps we have done
if (cat) {
fullCats.push(categoryId); //the category in question needs to be in the results as well
Category.getSubCategories(cat, cnt, fullCats, catSearchCallback, function(err, allCats) {
if (err) {
cb(err);
}
cb(null, allCats);
});
} else {
return categoryId;
}
});
}
答
以下似乎是工作拉开序幕,我测试了它在我的系统的零,一个和两个水平层次和它的期望是什么(到目前为止...)。
当然可能有更优雅的解决方案,更有效的解决方案等。 如果您有一个,非常欢迎您分享它。 对我来说,暂时,这工作:)
/**
* Recursive iteration functions for getting subcategories
*
* Starts with getAllSubCategories, and recursively call
* - getSubCategories
* - which in turn calls catSearchCallback as callback
*
* In order to avoid race conditions, we can't use a global variable...
* ...thus we need to pass the cumulated results (fullCats) and the
* running queue (queue) as parameters to all involved functions.
* The final "done" callback is also passed around until it's needed
*/
Category.getSubCategories = function(cat, queue, fullCats, cb, done) {
//load all subcategories of this the provided category is parent
Category.find({where: {parentId: cat.id}}, function(err, cats) {
if (err) {
logger.error(err);
cb(err);
}
if (cats.length > 0) {
cb(null, queue, fullCats, cats, cat.id, done);
} else {
cb(null, queue, fullCats, null, cat.id, done);
}
});
}
/**
* callback after every subCategory
*/
var catSearchCallback = function(err, queue, fullCats, cats, catId, done) {
if (err) {
logger.error(err);
done(err);
}
//first remove the returned category ID from the queue (means it has been processed)
var index = queue.indexOf(catId);
if (index > -1) {
queue.splice(index, 1);
} else {
//this should NOT HAPPEN!!!!
logger.warn("NO CAT FOUND FOR REMOVAL");
}
//now if there are subcategories in this category, go further down
if (cats) {
for (var c=0; c<cats.length; c++) {
//add this ID to the queue
queue.push(cats[c].id);
//add this ID to the final results
fullCats.push(cats[c].id);
//iterate this category
Category.getSubCategories(cats[c], queue, fullCats, catSearchCallback, done);
}
} else {
//there are no further subcategories for this category
//and if the queue is empty, we are done
if (queue.length == 0) {
done(null, fullCats);
}
}
}
/**
* start here for getting sub categories, provide categoryId from which to start
*/
Category.getAllSubCategories = function(categoryId, cb) {
Category.findById(categoryId, function(err, cat) {
if (err) {
return cb(err);
}
var fullCats = []; //this variable holds all collected IDs of categories which are subcategories of the given category
var queue = []; //this array stores the IDs which have been queried; when a category is queried by parent, its ID is added;
//after returning from the callback, its ID is removed from here in order to clean the queue;
//we know that when the queue has become empty that all async calls have terminated
if (cat) {
//the category ID for which we are getting all subcategories needs to be included in final results!
fullCats.push(categoryId);
//add the first to the queye
queue.push(categoryId);
//now begin the recursive chain...
Category.getSubCategories(cat, queue, fullCats, catSearchCallback, function(err, allCats) {
if (err) {
cb(err);
}
//...and when finished, terminate the complete call with the callback
cb(null, allCats);
});
} else {
logger.info("Category.getAllSubCategories: category not found");
return categoryId;
}
});
}
你有没有尝试过使用承诺?我简化了回调的所有接线... – elclanrs
我对Promise的掌握太少,无法自己想出一个可行的解决方案....我正在学习它们的过程中,但还没有在那里 – faboolous
@faboolous尝试从这里开始:http ://bluebirdjs.com/docs/why-promises.html承诺将解决这个问题。 –