2016-01-21 61 views
1

我有一個分層系統。一個類別有一個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承諾將解決這個問題。 –

回答

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