我有一个树形结构,我想用Promises走路,我还没有找到合适的代码模式。
假设我们给出了节点名称,并且将节点名称转换为节点的行为是异步过程(例如涉及Web访问)。还假设每个节点包含一个(可能为空)子项名称列表:
function getNodeAsync(node_name) {
// returns a Promise that produces a node
}
function childrenNamesOf(node) {
// returns a list of child node names
}
我希望最终得到一个带有此签名的方法:
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.
}
返回在根节点及其所有子节点被访问后履行的Promise,因此可以按如下方式调用:
walkTree("grandpa", function(node) { console.log("visiting " + node.name); })
.then(function(nodes) { console.log("found " + nodes.length + " nodes.")});
I've create a gist that shows my first attempt。我的(略微错误)的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);
});
};
以正确的顺序访问节点,但最外层的Promise在访问所有子节点之前解析。 See the gist for full details
正如@MinusFour指出的那样,使用这种技术来平整节点列表是毫无意义的。事实上,我真的只想在访问所有节点时触发最后的承诺,因此更实际的用例是:
walkTree("grandpa", function(node) { console.log("visiting " + node.name); })
.then(function() { console.log("finished walking the tree")});
答案 0 :(得分:1)
处理每个节点的函数调用并不是一个大问题,但收集节点值是一个问题。有点难以走这棵树,你最好的选择是将它映射到没有最终值的树。你可以使用类似的东西:
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)))
})]);
}
从那里开始:
var flatTree = buildTree(root_name).then(flatArray);
flatTree.then(nodes => nodes.forEach(visit_fn));
flatTree.then(nodes => whateverYouWantToDoWithNodes);
要展平您可以使用的阵列:
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];
}
}
老实说,如果你想要一个节点列表,你最好将它展平然后迭代元素,那么拥有树步行器毫无意义,但如果你愿意,你可以走数组树。 / p>
答案 1 :(得分:0)
尽管我在O.P中说过,我并不关心最终承诺的返回值,但我做想要等到所有节点都被遍历。
原始尝试的问题只是map()函数中缺少return语句。 (尽管有外表,但这与@ 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);
});
};
以下是walkTree()的两个用例。第一个只是按顺序打印节点,然后在树遍历结束时宣布:
walkTree("grandpa", function(node) { console.log("visiting " + node.name); })
.then(function() { console.log("finished walking the tree")});
第二个创建一个平面的节点列表,在树步行完成时可用:
var nodes = [];
walkTree("grandpa", function(node) { nodes.push(node) })
.then(function() { console.log('found', nodes.length, 'nodes);
console.log('nodes = ', nodes); });