我偶尔遇到这种问题,目前我最终不得不重构大量代码来解决这个问题。我的问题是,是否有一个更简单的模式。
我得到了这个问题的印象,但我还没有找到答案。例如,SO显示this Q&A,但它显然不是同一个问题
短伪代码版本是这样的。我从命令式编程开始。
/**
* node: some node in a tree of nodes
* leave: an array on to which leaves will be pushed in depth first order
*/
function extractLeaves(node, leaves) {
switch (node.type) {
case 'leaf':
leaves.push(node);
break;
case 'special':
extractLeavesFromSpecial(node, leaves);
break;
default:
extractLeavesFromChildren(node, leaves);
break;
}
}
function extractLeavesFromChildren(node, leaves) {
node.children.forEach((child) => extractLeaves(child, leaves);
}
function extractLeavesFromSpecial(node, leaves) {
// skip leaves if they're this node is not special
if (isSomethingIsReallySpecial(node)) {
extractLeavesFromChildren(node, leaves);
}
}
const leaves = [];
extractLeavesFromChildren(root, leaves);
你可以看到它的命令性和递归性并且它正在工作
现在我突然想要使用一些在递归中使用回调的库。
case 'special':
extractLeavesFromSpecial(node, leaves);
break;
case 'newSpecial':
extractLeavesFromNewSpecial(node, leaves);
break;
function extractLeavesFromNewSpecial(node, leaves) {
someLibraryFunctionThatsAsync(node, callback); // UGH!!!
....
解决方案似乎是承诺,这很好,我可以做到。
我的问题是,我是否必须重构每个功能?
AFAICT当我遇到异步所需的解决方案时,我突然不得不重新编写所有函数以使用promises all,因为其中只有一个最终是异步的。
function extractLeaves(node) {
return new Promise(resolve => {
switch (node.type) {
case 'leaf':
resolve([node]);
break;
case 'special':
resolve(extractLeavesFromSpecial(node);
break;
case 'newSpecial':
resolve(extractLeavesFromNewSpecial(node));
break;
default:
resolve(extractLeavesFromChildren(node);
break;
}
};
}
function extractLeavesFromChildren(node) {
return Promise.all(node.children.map(child => {
return extractLeaves(child, leaves);
}).then(array => {
// have to flatten as each element is an array
// this will also magically filter out empty arrays
return [].concat.apply([], arrays);
});
}
function extractLeavesFromSpecial(node) {
// skip leaves if they're this node is not special
if (isSomethingIsReallySpecial(node)) {
return extractLeavesFromChildren(node);
} else {
return Promise.resolve([]);
}
}
function extractLeavesFromNewSpecial(node) {
return new Promise((resolve, reject) => {
someLibraryFunctionThatsAsync(node, (err, result) => {
if (err) {
reject(err);
} else {
resolve(...); // not important what this returns to the point
}
}
....
}
在我的实际代码中,我有大约8个函数,是真的需要什么还是有一些更简单的模式?
注意:在我的情况下,不仅这似乎需要重构一切。它似乎使解决方案稍微复杂一些,因为保持叶子列表平坦需要过滤并检查嵌套路径是否在叶子中结束,而旧方法没有这个问题。这是一条线,但必须考虑问题之前的问题尚不存在。
第二个问题是在命令式方法中,一次处理一棵树很容易,如
lotsOfTrees.forEach(tree => {
const leaves = [];
extractLeaves(tree, leaves);
// do something with leaves.
});
在新的内容虽然每个都是一个承诺,因此没有更多的工作将同时处理(更多的内存),这意味着更多的重构(不是一个大问题,它就像3 -6行代码使得承诺按顺序运行,而不是需要进行大量的重构,以便在我做错事情时提出问题。
从某种意义上说,似乎很明显,因为一个深度函数需要异步才能有效地使所有函数在它之上异步。这意味着所有这些功能都必须重构。我只是仔细检查,我不会错过更简单的事情。