【问题标题】:Returning a value from an asynchronous recursive function从异步递归函数返回一个值
【发布时间】:2014-05-01 10:47:59
【问题描述】:

我在 Node.js 中有一个复杂的递归函数,为了这个问题,我将其简化为以下内容:

function sum(tree) {
    if (!tree) return 0;
    var sumLeft = sum(tree.left);
    var sumRight = sum(tree.right);
    return sumLeft + tree.val + sumRight;
}

var exampleTree = {
    val: 3,
    right: { val: 4 },
    left: { val: 5, 
        right: {val: 6},
        left: {val: 7}
    }
}

console.log(sum(exampleTree)); // returns 25 = 3+4+5+6+7

现在我想将函数“sum”转换为异步函数:

function sumAsync(tree, callback) {
    // ???
}

但是,由于该函数现在没有返回值,我不知道如何获取值 sumRight 和 sumLeft 并将它们组合起来。

一种可能的选择是完全重写算法,使其是迭代的而不是递归的(正如这个问题中所建议的那样:how to make this synchronous recursive function asynchronous)。但是,在许多情况下,这可能非常复杂,并且会使整个程序不可读。有没有一种解决方案可以保持递归函数的简单结构,同时使其异步?

注意:我的问题不是对树中的值求和...tree-sum 函数只是一个最小的工作示例。

编辑:根据 vkurchatkin 的回答和评论,我决定使用 async.parallel。结果如下:

var async = require("async");


function sumAsync (tree, callback) {
    if (!tree) return setImmediate(callback.bind(null, null, 0));

    async.parallel([
      sumAsync.bind(this, tree.left),
      sumAsync.bind(this, tree.right),
    ],
    function(err, results) {
        callback(err, tree.val+results[0]+results[1]);
    });
}

sumAsync(exampleTree, function(err, result) {
    console.log(result);  // prints 25
});

【问题讨论】:

  • 您是否尝试继续传递回调,当您到达树结构的末尾时调用它。虽然解析这棵树的异步行为看起来有点太多了..
  • 你能举个例子吗?

标签: node.js asynchronous recursion


【解决方案1】:

这样的事情可能会奏效:

function sumAsync (tree, callback) {
    if (!tree) return setImmediate(callback.bind(null, null, 0));

    var pending = 3;
    var sum = 0;
    var done = false;

    function handleError (err) {
       if (!done) {
          callback(err);
          done = true;
        }
    }

    function _callback (err, res) {
      if (err) return handleError(err);

      sum += res;

      if (!--pending && !done) {
        done = true;
        callback(null, sum);
      }
    }

    tree.fetchLeft(function (err, left) {
      if (err) return handleError(err);

      sumAsync(left, _callback);
    });

    tree.fetchRight(function (err, right) {
      if (err) return handleError(err);

      sumAsync(right, _callback);
    });

    tree.fetchValue(_callback);
}

它可能看起来很复杂,但实际上它只是一个临时的 async.parallel 实现,因此您可以直接使用它。

【讨论】:

【解决方案2】:

假设树结构从一开始就在那里,而不是一块一块地获取(我认为这样做是一种非常糟糕的做法)。

var exampleTree = {
    val: 3,
    right: { val: 4 },
    left: { val: 5, 
        right: {val: 6},
        left: {val: 7}
    }
}

function sum(tree,callback){
 if(!tree){ 
  if(callback!==undefined){ return callback(0); }
  return 0;
 }
 var sumLeft=sum(tree.left);
 var sumRight=sum(tree.right);
 var v = sumLeft+tree.val+sumRight;

 // callback is **not** passed so we continue
 if(callback===undefined){
  return v;
 }else{ // callback is passed meaning this is the root, end this.
  return callback(v);
 }
};

sum(null,function(result){
  console.log(result); // logs 0
});

sum(exampleTree,function(result){
    console.log(result); // logs 25
});

console.log(sum(exampleTree)); // logs 25

【讨论】:

  • 谢谢。该解决方案的优点是它可以异步和同步运行。另一方面,它的缺点是它不是完全异步的 - 所有工作都在一个步骤中完成而不会产生。因此,如果树非常大,它可能会暂停事件队列很长时间。
  • 实际上我认为不,它会消耗相同的内存,但它不会等待函数完成,所以在队列中并发量超过高它将并行运行,它会处理其他任务直到这个返回
猜你喜欢
  • 1970-01-01
  • 2012-10-16
  • 1970-01-01
  • 2021-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-14
相关资源
最近更新 更多