异步递归调用我逼疯

问题描述 投票:2回答:2

我有一个等级制度。一个类别具有parentId的(这是0,如果它是一个根类别)。

示例行中的类别的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;
      }
    });
  }
javascript asynchronous recursion loopbackjs
2个回答
0
投票

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

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

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

0
投票

我没有数据库的工作,而不是完整的代码为好,例如我不知道Category.find是如何工作的。但随着人们在评论中说,这是绝对可以使用Promise一个代码,而我们在它,为什么不async / await。我无法运行此代码自己,所以考虑较完整的代码更蓝图/算法。所以,在这里不用什么。

与异步重写代码/等待:

Category.findAsync = function(condition){
    return new Promise((resolve, reject) => Category.find(
        condition,
        (err, result) => err ? reject(err) : resolve(result)
    ))
}

Category.getSubCategories = async function(parentId){
    return [parentId].concat(Promise.all(
        (await Category.findAsync({where: {parentId}})).map(
            subCategory => Category.getSubCategories(subCategory.id)
        )
    ))
}

运行:

(async()=>{
    const categoryId = 12345
    console.log(await Category.getSubCategories(categoryId))
})()

因为,我不知道该怎么find工作,我已经写了一个包装围绕它叫做findAsync没有,将其转换回调承诺。

*回答结束*

PS:号召所有等待/无极专家编辑这个答案,并帮助OP达成有效的解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.