【问题标题】:Recursive promises to create tree递归承诺创建树
【发布时间】:2017-09-21 22:06:59
【问题描述】:

当节点元素传递给它时,树 api 返回子节点。

我首先传递根节点,然后根据返回的节点递归地传递所有节点,如果它们的 hasChildren 参数为 true。

有没有办法知道函数何时完成创建树。

function recursivelyFetchChildren(id, selectedAsset, parentId){
    return ajaxCall(id, selectedAsset, parentId)
    .then(function(data){
        //collects all childs in childs array
        childs.push(data);
        for(var i=0; i<data.length; i++){
            if(data[i].HasChildren){
                return recursivelyFetchChildren(id,selectedAsset,data[i].Id);
            }else{
                //this return statement prematurely completes the promise
                return data[i];
            }
        }
    });
}

recursivelyFetchChildren(id, selectedAsset, parentId).then(function(){
    print(childs)  //prints the childs before all the promises have resolved
});

谁能提出一种方法,让我可以让 recursivelyFetchChildren 函数等待渲染完整的树?

【问题讨论】:

    标签: javascript asynchronous recursion promise


    【解决方案1】:

    换一种方式,返回一个函数,当它的所有子元素都被解析时,它就被解析了。

    {
      id: "123",
      data: {
        children: [
          { id: "234", value: 2, data: { children: [] } },
        ]
      }
    }
    
    
    const loadData = node => getData(`/url/${node.id}`).then(data => {
      return loadChildren(data.hasChildren ? data.children : [])
        .then(children => {
          data.children = children;
          node.data = data;
          return node;
        });
    });
    const loadChildren = children => Promise.all(children.map(loadData));
    

    因为我正在返回并链接承诺,所以我返回的最外层不会解决,直到最内层完成。

    就个人而言,我可能会构建一个新节点,而不是修改我拥有的节点,但这完全是另一回事。

    编辑

    相互递归:两个函数相互调用。

    function a () { return b(); }
    function b () { return a(); }
    

    如果你有一棵树,其中每个根都有一个子数组,那么你可以编写两个函数在彼此之间来回反弹:一个处理一个节点,一个处理一个节点数组。当然还有其他例子,但一个是你想要的递归函数,另一个是循环,或者让你一次调用一个递归函数的东西……通常;同样,还有其他情况。

    const tree = {
      value: 1,
      children: [{
        value: 2,
        children: []
      }, {
        value: 3,
        children: []
      }]
    };
    
    const sumChildren = children =>
      children
        .map(sumNode)
        .reduce((x, y) => x + y, 0);
    
    const sumNode = node =>
      node.value + sumChildren(node.children);
    
    const total = sumNode(tree); // 6
    

    承诺解决方案

    就 Promise 而言,通常会遗漏的一件事是,您可以在 .then 内返回 Promise,并导致 Promise 的解析等待更长时间。当它解析时,它将具有 b 解析的值(或 b 的错误)

    eventuallyGetA()
      .then(a => eventuallyGetB(a.data))
      .then(b => console.log(`I waited for ${b}`));
    

    你甚至可以做类似的事情

    const wait = ms =>
      new Promise(resolve => setTimeout(resolve, ms));
    
    doX()
      .then(() => wait(2000))
      .then(doY)
      .then(() => wait(3000))
      .then(doZ);
    

    该序列将要做什么应该非常简单。

    希望对您有所帮助。

    【讨论】:

    • 但是,只有在 HasChildren 属性为 true 的情况下,我如何设置进行 ajax 调用的条件。你能编辑我的例子并解释一下吗?我无法理解你给出的例子。
    • @BirjuShah 如果您有标志,它不会进行 AJAX 调用......如果您在节点上调用该函数,它会进行 AJAX 调用。如果一个节点在其数组中没有子节点,则它没有要循环的子节点。如果它不能循环任何东西,那么它就不会调用 AJAX 函数,因为它不会递归下降。这意味着 HasChildren 属性可能毫无用处,当一个空列表用于相同目的时。但为了明确起见,我在示例中添加了hasChildren 检查,这实际上并没有增加任何安全性;一个空数组就足够了。
    • 谢谢。有用。但是,我很难理解它。如果你能发表一些解释这个概念的文章,那就太好了。
    • @BirjuShah 有两个概念:相互递归(或“A - B 递归”),如果您在 .then 回调中返回一个承诺,则外部承诺不会完成,直到内在承诺完成。我承认,如果您从没有同时掌握两者的角度来看,这会很令人困惑。但是有很多孤立的例子。如果您了解了处理树的相互递归,并且即使通过在 .then 中返回 Promise 也了解了链式 Promise,那么您就拥有了将其组合在一起所需的所有部分。
    • @BirjuShah 进行了编辑;希望你觉得它有用。
    猜你喜欢
    • 2014-02-04
    • 1970-01-01
    • 2018-05-29
    • 1970-01-01
    • 1970-01-01
    • 2019-05-27
    • 2014-07-24
    相关资源
    最近更新 更多