简体   繁体   English

与Promises一起走树

[英]walking a tree with Promises

I have a tree structure that I want to walk using Promises, and I haven't figured out the right code pattern for it. 我有一个树结构,我想使用Promises走路,我还没有想出正确的代码模式。

Assume that we're given node names, and the act of converting a node name to a node is an asynchronous process (eg involves a web access). 假设我们给出了节点名称,并且将节点名称转换为节点的行为是异步过程(例如涉及Web访问)。 Also assume that each node contains a (possibly empty) list of children names: 还假设每个节点包含一个(可能为空)子项名称列表:

function getNodeAsync(node_name) {
    // returns a Promise that produces a node
}

function childrenNamesOf(node) {
    // returns a list of child node names
}

What I want to end up with a method with this signature: 我希望最终得到一个带有此签名的方法:

function walkTree(root_name, visit_fn) {
   // call visit_fn(root_node), then call walkTree() on each of the
   // childrenNamesOf(root_node), returning a Promise that is fulfilled
   // after the root_node and all of its children have been visited.
}

that returns a Promise that's fulfilled after the root node and all of its children have been visited, so it might be called as follows: 返回在根节点及其所有子节点被访问后实现的Promise,因此可以按如下方式调用:

walkTree("grandpa", function(node) { console.log("visiting " + node.name); })
  .then(function(nodes) { console.log("found " + nodes.length + " nodes.")});

update 更新

I've create a gist that shows my first attempt . 我创造了一个显示我第一次尝试的要点 My (slightly buggy) implementation for walkTree() is: 我的(略微错误)的walkTree()实现是:

function walkTree(node_name, visit_fn) {
    return getNodeAsync(node_name)
        .then(function(node) {
            visit_fn(node);
            var child_names = childrenNamesOf(node);
            var promises = child_names.map(function(child_name) {
                walkTree(child_name, visit_fn);
            });
            return Promise.all(promises);
        });
};

This visits the nodes in the correct order, but the outermost Promise resolves before all the sub-nodes have been visited. 这将以正确的顺序访问节点,但最外层的Promise在访问所有子节点之前解析。 See the gist for full details . 有关详细信息,请参阅要点

And as @MinusFour points out, using this technique to flatten the list of nodes is rather pointless. 正如@MinusFour指出的那样,使用这种技术来平整节点列表是毫无意义的。 In fact, I really just want the final promise to fire when all the nodes have been visited, so a more realistic use case is: 事实上,我真的只想在访问所有节点时触发最后的承诺,因此更实际的用例是:

walkTree("grandpa", function(node) { console.log("visiting " + node.name); })
  .then(function() { console.log("finished walking the tree")});

Well it isn't much of a problem to handle a function call for each node, but gathering the node values is kind of a problem. 那么处理每个节点的函数调用并不是一个问题,但收集节点值是一个问题。 Kind of hard to walk that tree, your best bet would be to map it to a tree with no eventual values. 有点难以走这棵树,你最好的选择是将它映射到没有最终值的树。 You could use something like: 你可以使用类似的东西:

function buildTree(root_name) {
    var prom = getNodeAsync(root_name);
    return Promise.all([prom, prom.then(function(n){
        return Promise.all(childrenNamesOf(n).map(child => buildTree(child)))
    })]);
}

From there on you do: 从那以后你做:

var flatTree = buildTree(root_name).then(flatArray);
flatTree.then(nodes => nodes.forEach(visit_fn));
flatTree.then(nodes => whateverYouWantToDoWithNodes);

To flatten the array you could use: 要展平阵列,您可以使用:

function flatArray(nodes){
    if(Array.isArray(nodes) && nodes.length){
        return nodes.reduce(function(n, a){
                return flatArray(n).concat(flatArray(a));
        });
    } else {
        return Array.isArray(nodes) ? nodes : [nodes];
    }
}

To be honest, it's pointless to have the tree walker if you want a list of nodes you are better up flattening it up and then iterating the elements, but you can walk the array tree if you want. 说实话,如果你想要一个节点列表,你最好将它展平然后迭代元素,那么拥有树步行器毫无意义,但如果你愿意,你可以走数组树。

Despite what I said in the OP, I don't really care about the return values of the final promise, but I do want to wait until all the nodes have been traversed. 尽管我在OP中说过,我并不真正关心最终承诺的返回值,但我确实想要等到所有节点都被遍历。

The problem with the original attempt was simply a missing return statement in the map() function. 原始尝试的问题只是map()函数中缺少return语句。 (Despite appearances, this is essentially structurally identical to @MinusFour's answer.) Corrected form below: (尽管有外观,但这与@ MinusFour的答案在结构上完全相同。)修正后的表格如下:

function walkTree(node_name, visit_fn) {
    return getNodeAsync(node_name)
        .then(function(node) {
            visit_fn(node);
            var child_names = childrenNamesOf(node);
            var promises = child_names.map(function(child_name) {
                return walkTree(child_name, visit_fn);
            });
            return Promise.all(promises);
        });
};

Here are two use cases for walkTree(). 以下是walkTree()的两个用例。 The first simply prints the nodes in order then announces when the tree walk is finished: 第一个简单地按顺序打印节点,然后在树遍历结束时宣布:

walkTree("grandpa", function(node) { console.log("visiting " + node.name); })
  .then(function() { console.log("finished walking the tree")});

The second creates a flat list of nodes, made available when the tree walk completes: 第二个创建一个平面的节点列表,在树遍历完成时可用:

var nodes = [];
walkTree("grandpa", function(node) { nodes.push(node) })
  .then(function() { console.log('found', nodes.length, 'nodes);
                     console.log('nodes = ', nodes); });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM