具有Promise的Nodejs中的遍历树

时间:2018-02-17 18:32:01

标签: node.js tree promise

给出一个简单的用户树:

users:

id | name  | parent_id
1  | Bob   | NULL
2  | Jan   | 1
3  | Mat   | 2
4  | Irene | 2
5  | Ellie | 2
6  | Laura | 5
7  | Uma   | 6

我正试图用这段代码来实现它:

addChildren: function(db, treeUsers, parentId) {
  const main = this;
  var sql = Sql.select().from('users')
    .where('parent_id = ?', parentId)
    .toParam();

  return db.execute(sql.text, sql.values)
    .then(function([rs,meta]) {
      rs.map(function(obj) { 
          treeUsers[obj.id] = obj;
      });

      return treeUsers;
  });
},

getTreeUsers: function(db, depth) {
  const main = this;
  const rootNodeId = 1;

  var treeUsers = {};
  const getChildren = function(parentId) {
    return main.addChildren(db, treeUsers, parentId).then(function(children) {
      treeUsers = Object.assign(treeUsers, children);
      --depth;
      if(depth < 1) {
        return Promise.resolve(treeUsers);
      }

      return Promise.all(Object.keys(children).map(function(id) { 
          return getChildren(id); 
      }));

  };

  return getChildren(rootNodeId);
});

所需的输出是这样的:

{
  '2': { id: 2, name: 'Jan'  , parent_id: 2 },
  '3': { id: 3, name: 'Mat'  , parent_id: 2 },
  '4': { id: 4, name: 'Irene', parent_id: 2 },
  '5': { id: 5, name: 'Ellie', parent_id: 2 },
  '6': { id: 6, name: 'Laura', parent_id: 5 },
  '7': { id: 7, name: 'Uma'  , parent_id: 6 }
}

现在我变得更像这样:

[[     {
  '2': { id: 2, name: 'Jan'  , parent_id: 2 },
  '3': { id: 3, name: 'Mat'  , parent_id: 2 },
  '4': { id: 4, name: 'Irene', parent_id: 2 },
  '5': { id: 5, name: 'Ellie', parent_id: 2 },
  '6': { id: 6, name: 'Laura', parent_id: 5 },
  '7': { id: 7, name: 'Uma'  , parent_id: 6 }
} ],
[     {
  '2': { id: 2, name: 'Jan'  , parent_id: 2 },
  '3': { id: 3, name: 'Mat'  , parent_id: 2 },
  '4': { id: 4, name: 'Irene', parent_id: 2 },
  '5': { id: 5, name: 'Ellie', parent_id: 2 },
  '6': { id: 6, name: 'Laura', parent_id: 5 },
  '7': { id: 7, name: 'Uma'  , parent_id: 6 }
} ],
[     {
  '2': { id: 2, name: 'Jan'  , parent_id: 2 },
  '3': { id: 3, name: 'Mat'  , parent_id: 2 },
  '4': { id: 4, name: 'Irene', parent_id: 2 },
  '5': { id: 5, name: 'Ellie', parent_id: 2 },
  '6': { id: 6, name: 'Laura', parent_id: 5 },
  '7': { id: 7, name: 'Uma'  , parent_id: 6 }
} ] ... ]

我很肯定问题是Promise.all(getChildren(..))调用,但我不确定如何重构,以便我完全回到最后的结果集。

2 个答案:

答案 0 :(得分:1)

一些问题:

  • 您在两种方法中都会改变treeUsers对象,这意味着您解决的对象以后仍会发生变异。请注意,对于getTreeUsers的一次调用,您只能拥有该对象的一个实例。请注意以下内容中的分配:

    treeUsers = Object.assign(treeUsers, children);
    

    没有做任何事情,因为Object.assign已经改变了第一个参数。

  • Promise.all解析为数组值,其中每个元素都包含treeUsers对象。当它被所有单独的调用变异时,所有这些数组条目都指向相同的对象引用,解释了你看到的重复。
  • depth是每次调用getChildren时共有的变量。将它作为参数传递会更安全,以确保兄弟姐妹使用depth的相同值进行扩展

我只是避免传递treeUsers对象,因为它应该只是解析后的值。让每个承诺解决孩子们可以&#34;看到&#34;并在每个Promise.all承诺解决后整合结果。

以下是这样的看法:

addChildren: function(db, treeUsers, parentId) {
    const main = this,
        sql = Sql.select().from('users')
            .where('parent_id = ?', parentId)
            .toParam();
    return db.execute(sql.text, sql.values).then(function([rs,meta]) {
        // Create a new(!) object and return it
        return Object.assign(...rs.map(function(obj) { 
            return { [obj.id]: obj };
        }));
    });
},

getTreeUsers: function(db, depth) {
    const main = this,
        rootNodeId = 1;

    const getChildren = function(parentId, depth) { // pass depth
        return main.addChildren(db, parentId).then(function(children) {
            if (depth <= 1) {
                return children; // only the children. No need for Promise.resolve
            }
            return Promise.all(Object.keys(children).map(function(id) { 
                return getChildren(id, depth - 1); // pass depth 
            })).then(arr => Object.assign(children, ...arr)); // consolidate 
        });
    }
    return getChildren(rootNodeId, depth);
}

答案 1 :(得分:0)

是的,Promise.all始终返回所有结果值的数组。当你的调用都返回具有相同值treeUsers的promises时,你会获得这些对象的数组。由于您始终希望getChildren始终只返回该对象,因此您可以简化为

function getChildren(parentId) {
  return main.addChildren(db, treeUsers, parentId).then(function(children) {
    Object.assign(treeUsers, children);

    // when `children` is empty, the recursion terminates here
    return Promise.all(Object.keys(children).map(getChildren)).then(() =>
      treeUsers
    );
  });
}

(当然这不会产生树形结构)