在Javascript中递归遍历树

时间:2012-09-04 20:52:21

标签: javascript node.js asynchronous

这是用Java做的超级简单的任务,但javascript的异步性质使得这个任务(对我来说)几乎不可能,至少我现在的知识。(我不是想破坏javascript。喜欢这个语言!) 。

这是非常基本的。顶级树在我的mysql数据库中具有null的父级。很容易找到孩子。孩子们可以使用线路。树的深度是可变的。

    private static Set<Tree> getBranches( Tree trunk ) {

    Set<Tree> treeSet = new HashSet<Tree>();

    if ( trunk != null ) {

        if ( trunk.hasLines() ) { //queries if tree has lines.  returns true or false
            treeSet.add( trunk );
        }

        for ( Tree tree : trunk.treeList ) {
            treeSet.addAll( getBranches( tree ) );
        }
    }

    return treeSet;
}

基本上,该方法测试树是否有可用的行。如果是这样,它会将所有这些添加到集合中。如果没有,它会继续,直到它找到行。

mysql节点库的异步特性将此任务变为地狱。

这是我现在拥有的

   function hasLines(tree_id, callback) {
        var ret;
        pool.query('SELECT * from pkg_line_tree where tree_id = ?', [tree_id], function (err, rows) {

            if (rows.length > 0) {
                ret = true;
            } else {
                ret = false;
            }
            callback(ret);
        });
    }


     function dig(tree_id, treeArray, callback) {

        pool.query('SELECT * from tree where parent_id = ?', [tree_id], function (err, rows) {

           if (rows) {

              for (var i in rows) {
                 hasLines(rows[i].tree_id, function (t) {

                    if (t) {
                       treeArray.push(rows[i].tree_id);
                    } else {
                       treeArray.concat(dig(rows[i].tree_id, treeArray));
                    }
                 });
              }

              if (callback) {
                 callback(treeArray);
              }

           }
        });

        return treeArray;
     }


     var treeArray = [];
     dig(52, treeArray, function (t) {
        res.json(t);
     });

我真的只需输出这个根树中的所有可用子项。

如果这没有意义,请告诉我。我会尝试重构。我希望我能得到某种观点。我讨厌使用像Fibers这样的东西来完成这项任务,但我没有选择。感谢。

2 个答案:

答案 0 :(得分:2)

你必须使用异步https://github.com/caolan/async

我已修改您的挖掘功能以使用async&#39; forEach方法

function dig(tree_id, treeArray, AllDone) {
       pool.query('SELECT * from tree where parent_id = ?', [tree_id], function (err, rows) {
          if (rows) {
            async.forEach(
                rows,
                function(row, callback) {
                    hasLine(row.tree_id, function(t){
                        if (t) {
                            treeArray.push(row.tree_id);
                            callback();
                        }
                        else {
                            dig(row.tree_id, treeArray, callback);
                        }
                    });
                },
                function(err) {
                    if (err) AllDone(err, treeArray);
                    else AllDone(null, treeArray);
                });
            }
          else 
            AllDone(null, treeArray)
        });
}

treeArray = [];
dig(52, treeArray, function(err, t) {  
    res.json(t);  
});

假设rows是一个数组.. forEach遍历每一行并执行hasLine,每次迭代都会在完成时调用callback函数,并且AllDone将在所有callback时调用{1}}函数被调用。这里棘手的部分是递归,每个递归调用将有一个forEach循环,只有当所有AllDone完成时才会调用callbacks方法。

然而forEach并行执行,因此订单不会被保留

如果您不关心订单,我认为这应该有用。

修改您可以使用forEachSeries解决订单问题。

答案 1 :(得分:2)

您对dig()的使用目前不一致:

// asynchronous with callback
dig(52, treeArray, function (t) {
   res.json(t);
});

// then synchronous with `return`?
treeArray.concat(dig(rows[i].tree_id, treeArray));

此外,最后一行中的concat实际上并没有做太多,因为它不会改变它被调用的数组。您可能实际上不希望它dig绕过treeArray,而不是像treeSet中那样定义新的getBranches。所以,如果确实如此,那么每次都会将treeArray附加到自身的末尾。

您仍然可以将concat用于多个treeSet,但您必须存储其return值:

treeSet = treeSet.concat(subSet);

并且,你必须用异步迭代器替换for循环,因为循环不会在继续之前等待异步操作。 async library有一些选择,如果你想尝试它。

因此,对于多个treeSetconcatasync.forEachSeries,您可以尝试:

function dig(tree_id, callback) {
  var treeSet = [];

  hasLines(tree_id, function (yep) {
    if (yep) {
      treeSet.push(tree_id);
    }

    pool.query('SELECT * from tree where parent_id = ?', [tree_id], function (err, rows) {

      function each(row, next) {
        dig(row.tree_id, function (subSet) {
          treeSet = treeSet.concat(subSet);
          next(null);
        });
      }

      function done() {
        callback(treeSet);
      }

      async.forEachSeries(rows, each, done);
    });
  });
}

dig(52, function (treeSet) {
  res.json(treeSet);
});