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; 
     } 
    }); 
    } 
+1

你有没有尝试过使用承诺?我简化了回调的所有接线... – elclanrs

+0

我对Promise的掌握太少,无法自己想出一个可行的解决方案....我正在学习它们的过程中,但还没有在那里 – faboolous

+1

@faboolous尝试从这里开始:http ://bluebirdjs.com/docs/why-promises.html承诺将解决这个问题。 –

以下似乎是工作拉开序幕,我测试了它在我的系统的零,一个和两个水平层次和它的期望是什么(到目前为止...)。

当然可能有更优雅的解决方案,更有效的解决方案等。 如果您有一个,非常欢迎您分享它。 对我来说,暂时,这工作:)

/** 
    * 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; 
     } 
    }); 
    }