如果回调调用封闭函数,它会被称为递归调用吗?

时间:2015-01-20 03:50:56

标签: javascript node.js recursion

例如:

如果在http.request回调中进行了res.on('end')调用,那么该呼叫是递归的吗?

http.request(options, function(res) {
  res.on('data', function(chunk) {
    data+=chunk;
  });

  res.on('end', function(){
    //do some stuff
    http.request(options, function(res) {...});//is this recursive?
  });
}).end();

编辑:

让我们举一个更简单的例子:假设有一个函数可以逐个字符地读取文件:

var noOfChar = 10;
var i = 0;
readChar(function processChar(char){
  if(i < noOfChar) {
    console.log(char);
    i++;
    readChar(processChar); //is this recursive?
  }
}

4 个答案:

答案 0 :(得分:2)

让我们考虑一下代码的一些具体属性,以及它与人们通常所说的“递归”的相似或不同之处,而不是争论它的标签。

通常,递归函数会在每一步增加堆栈(尾部调用递归除外,它使用一种技巧来阻止增长)。但是在节点中,异步回调会丢弃外部上下文的堆栈。 (尝试在回调中引发异常并自己查看。)因此,此代码不会增加堆栈。

通常,递归函数也会调用自身,但我不会在您的示例中看到任何地方发生这种情况。 http上的两个听众是不同的功能。

第二个例子不是直接调用自己,而是间接调用自己。你有一个“基本案例”(noOfChar >= 10),此时递归会展开。如果readChar是同步的,你甚至会增加堆栈。所以这似乎更接近递归。

请注意,在第二个示例中,您有一个命名函数,而第一个示例只有匿名函数。一般来说,如果没有命名函数(或者至少是一些保存函数的变量),我不认为递归是可能的,因为否则函数如何引用自身?

答案 1 :(得分:0)

如果您要将不同的callback传递给它,我认为它不会递归。

如果你将同一个callback传递给它们,它将是递归的。

递归表示我是否在函数定义中调用相同的函数。但是如果你打算通过不同的回调,它将会像波纹管那样。

假设您有2个不同的回调callback1 and callback2

您已将第一个回调传递给外部http.request,并将第二个回调传递给内部http.request,因此外部http.request将执行callback1中给出的代码,但内部将是执行callback2中给出的代码。

答案 2 :(得分:0)

我不明白问题的含义 如果答案是肯定的,你会被老板鞭打致死吗? 是否有针对递归的法律?

http.request只是达到目的的手段。无论如何,您需要多次调用它来处理交易 您的示例代码的问题在于您在一大块代码中处理异步交换,它直接处理响应而不跟踪事务的状态。

我想到的唯一评论是这种逻辑非常脆弱,因为终止条件取决于传递给您的请求的任何参数,从而使大门敞开,导致一些潜在的问题,例如不同步答案和无限循环(他们是否应该得到&#34;递归调用&#34的名称;如果他们最终挂起系统则没什么兴趣。)

我宁愿建议构建一个显式状态机来跟踪预期的HTTP交换的进度,这将解决关于递归性和相关的“良好实践”的哲学问题。

答案 3 :(得分:0)

我会尝试回答我自己的问题,因为我觉得现在我更了解递归。也许这会帮助别人。

我怀疑是否一个函数调用一个回调函数,该函数再次调用该函数,它将被称为递归。我觉得它类似于indirect recursion,其中function1调用function2而function2再次调用function1。因此,如果function1调用function2调用function3,而function3又调用function1,它仍然是递归的,这并不重要。同样,如果函数调用一个调用该调用该函数的另一个回调的回调,则不应该这么重要。如果第二个例子是递归的,那么首先也可以是递归的。

问题的递归解决方案必须解决问题的解决方法,而不是像堆栈增长这样的语言实现。考虑tail-recursion,它不会增加调用堆栈,但没有人质疑它的递归。

所以关键是要递归思考。解决整个问题,如果其中一部分可以像原始问题一样解决,那么解决方案就是递归。调用自身的函数可能不符合递归的条件。有些人可能称之为自引用函数但不是递归函数。

递归至少应该包含一个基本情况和一个递归的情况,每个自引用应该使输入值更接近基本情况,这将结束递归。所以我认为提到的两个例子问题不是递归的

递归调用可能不会导致调用堆栈增加。有一个尾调用递归,它使调用堆栈大小保持不变。

采用以下由回调组成的示例,此处外部函数堆栈帧可能在内存中,也可能不在内存中。

var input = [[1,2,3],[4,5,6],[7,8,9]];

function double(ip) { 
  return ip.map(function (element) {
    if(Array.isArray(element)) {
      return double(element);
    }
    else {
      return 2 * element;
    }
  });
}

console.log(double(input));

我称之为递归。我们也可以执行Anonymous Recursion,因此无需明确命名函数。