首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用递归javascript承诺在树中搜索节点

使用递归javascript承诺在树中搜索节点
EN

Stack Overflow用户
提问于 2017-02-04 21:46:17
回答 2查看 969关注 0票数 0

我困在基于Javascript承诺的程序中,无法找到如何让它以承诺格式返回正确的值。

给定返回节点子节点的Tree作为承诺。例:

代码语言:javascript
运行
复制
tree.getChildren('/2/4')
.then(function (nodes) {    
    console.log(nodes); // logs 7,8,9 as children of node 4
}

使用tree.getChildren方法,searchNode方法应该递归地尝试在树中查找searchValue,如果找到它的路径,则返回路径,否则返回null

下面的方法只是尝试搜索树路径中的节点,但它只是返回undefined,我认为这是由于该方法的异步性质造成的。我如何重写代码以兑现承诺并获得期望的行为?

代码语言:javascript
运行
复制
function searchNode(searchValue, path){
    tree.getChildren(path).then(function(children){
        if(children.length>0){
            for(var i = 0; i<children.length; i++){
                if(children[i] == searchValue){
                    return path;
                } else {
                    childPath = searchNode(searchValue, path+"/"+children[i]);
                    if(childPath != null)
                        return childPath;
                }
            }
        }
        else{
            return null;
        }
    })
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-02-04 22:40:35

当结果异步可用时,函数searchNode将需要返回一个承诺。

但在两个地方,你不能这样做:

  1. 您应该返回对tree.getChildren的第一个调用
  2. 应该将searchNode的(递归)返回值作为承诺处理,但将其视为同步结果: childPath = searchNode(searchValue,path+"/"+childreni);if (childPath != null) { // ...etc。 这是不可能的返回值应该是一个承诺,因此您需要调用它上的then方法来获得返回值。

当您需要迭代几个,也许是所有的子程序时,您将得到同样多的承诺。但你可以而且应该只回一个承诺。

虽然您可以在找不到值的情况下返回null,但在我看来,在这种情况下,它更符合承诺产生被拒绝的承诺的想法。

因此,你可以得到第一个承诺的结果,然后如果它解决了,返回那个承诺,但如果它拒绝(即找不到),你应该连锁下一个承诺,下一个,.直到一个人解决了,然后把那个还回去。如果他们都没有决心,或者没有孩子,就应该回报一个被拒绝的承诺。

以下是建议的代码:

代码语言:javascript
运行
复制
function searchNode(searchValue, path){
    return tree.getChildren(path).then(function(children){
        // First look for the value among the immediate children
        for(let i = 0; i<children.length; i++){
            if(children[i] == searchValue){
                return path;
            }
        }
    // Then look deeper
    return (function loop(i) {
        if (i >= children.length) {
            return Promise.reject("not found"); 
        } else { 
            // after asynchronous result comes in, either
            // continue the loop, or resolve with path
            return searchNode(searchValue, path+"/"+children[i])
                .catch(loop.bind(null, i+1));
        }
    })(0);
}

代码语言:javascript
运行
复制
"use strict";
// Simple implementation of tree
const tree = {
    root: {
        '2': {
            '4': {
                '1': {}
            },
            '7': {}
        },
        '0': {}
    },
    getChildren: function(path) {
        return new Promise(function (resolve) {
            const node = path.split('/').reduce(function (parent, node) {
                return node === '' || !parent ? parent : parent[node];
            }, tree.root);
            resolve(Object.keys(node));
        });
    }
};

function searchNode(searchValue, path){
    return tree.getChildren(path).then(function(children){
        // First look for the value in the immediate children
        for(let i = 0; i<children.length; i++){
            if(children[i] == searchValue){
                return path;
            }
        }
        // Then look deeper
        return (function loop(i) {
            if (i >= children.length) {
                return Promise.reject("not found"); 
            } else { 
                // after asynchronous result comes in, either
                // continue the loop, or resolve with path
                return searchNode(searchValue, path+"/"+children[i])
                    .catch(loop.bind(null, i+1));
            }
        })(0);
    })
}

// Demo
searchNode('1', '').then(function (path) {
    console.log(path);
}, function (reason) {
    console.log(reason);
});

并行搜索方案

上述解决方案不会在孩子们中间平行地观察。相反,它将等待一个节点的搜索结果,然后决定是否应该对下一个同级节点启动搜索。根据tree.getChildren实现的实际异步性,这可能效率很低:

想象一棵树,其中第一个子节点有一个由1000个节点组成的多层子树,而要查找的值是第二个子节点的直接后代。如果搜索是并行启动的,那么对于这种情况,您将期望更早地得到结果。另一方面,一旦找到值,上述代码将不会搜索其他同级节点,而通过并行搜索,即使在找到值并解决了主要承诺之后,搜索仍将在“背景”(异步)中继续进行。因此,我们应该确保在找到值时不会启动更深层次的搜索。

为了实现这种并行搜索思想,您将立即在所有子节点上启动searchNode,并在每个子节点上应用then方法来监视哪个解析为第一个。

为此,您实际上可以定义两个通用实用程序方法-- Promise.notPromise.some --就像已经有Promise.racePromise.all一样。这些函数可以在其他类似于"Resolve ES6 Promise with first success?"的问答中找到

代码语言:javascript
运行
复制
// Invert the outcome of a promise
Promise.not = promise => promise.then(x => {throw x}, e => e);
// Resolve when the first promise resolves
Promise.some = iterable => Promise.not(Promise.all(iterable.map(Promise.not)));

或者,您可以使用库解决方案来解决这个问题,比如Promise.any

然后,您需要添加一些机制,以便在主承诺已经用已找到的值解决时,停止启动更深层次的搜索。对于这一点,只要听主要的承诺和标志,当它解决。这可用于停止异步代码以启动任何进一步的搜索。

下面是如何在您的情况下使用该Promise.some函数:

代码语言:javascript
运行
复制
function searchNode(searchValue, path){
    let resolved = false;

    return (function searchNodeSub(path) {
        return tree.getChildren(path).then(function(children){
            // If we already found the value via another parallel search, quit
            return resolved ? true
                // Otherwise look for the value among the immediate children
                : children.some(child => child == searchValue) ? path
                // If not found there, look deeper -- in parallel
                : Promise.some(children.map(child => searchNodeSub(path+"/"+child)));
        })
    })(path).then( path => (resolved = true, path) ); // register that we have a result
}

注意children.somePromise.some之间的相似之处。

代码语言:javascript
运行
复制
"use strict";
// Simple implementation of tree
const tree = {
    root: {
        '2': {
            '4': {
                '1': {}
            },
            '7': {}
        },
        '0': {}
    },
    getChildren: function(path) {
        return new Promise(function (resolve) {
            let node = path.split('/').reduce(function (parent, node) {
                return node === '' || !parent ? parent : parent[node];
            }, tree.root);
            resolve(Object.keys(node));
        });
    }
};

// Invert the outcome of a promise
Promise.not = promise => promise.then(x => {throw x}, e => e);
// Resolve when the first promise resolves
Promise.some = iterable => Promise.not(Promise.all(iterable.map(Promise.not)));

function searchNode(searchValue, path){
    let resolved = false;
    
    return (function searchNodeSub(path) {
        return tree.getChildren(path).then(function(children){
            // If we already found the value via another parallel search, quit
            return resolved ? true
                // Otherwise look for the value among the immediate children
                : children.some(child => child == searchValue) ? path
                // If not found there, look deeper -- in parallel
                : Promise.some(children.map(child => searchNodeSub(path+"/"+child)));
        })
    })(path).then( path => (resolved = true, path) ); // register that we have a result
}

// Demo
searchNode('1', '').then(function (path) {
    console.log(path);
}, function (reason) {
    console.log('Not found');
});

票数 3
EN

Stack Overflow用户

发布于 2017-02-04 22:07:15

您需要在.getChildren()中返回searchNode承诺,以便在返回childPath时等待它得到解决。

代码语言:javascript
运行
复制
function searchNode(searchValue, path){
  return tree.getChildren(path).then(function(children){ //return the promise so we can later wait for it to resolve
    if(children.length>0){
        for(var i = 0; i<children.length; i++){
            if(children[i] == searchValue){
                return path;
            } else {
                return searchNode(searchValue, path+"/"+children[i])
                  .then(function(childPath){ //wait for searchNode to complete
                    if (childPath != null) {
                      return childPath;
                    }
                  });
            }
        }
    }
    else{
        return null;
    }
  })
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42045988

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档