Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/425.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript如何管理递归调用?_Javascript - Fatal编程技术网

Javascript如何管理递归调用?

Javascript如何管理递归调用?,javascript,Javascript,我在玩JavaScript,注意到一个奇怪的行为(至少对我来说是奇怪的…) 所以我做了一个实验,它是这样的: 我有一个名为“myDiv”的div 实例: 所以我正在修改div上的文本,结果是文本从9变为0,而我认为应该从0变为9,因为递归changeText(计数器)调用是在调用实际更改文本的方法之前进行的。该函数包含一个异步超时 setTimeout(function(){ recursiveCall(--counter);// calls the next function, whi

我在玩JavaScript,注意到一个奇怪的行为(至少对我来说是奇怪的…)

所以我做了一个实验,它是这样的:

我有一个名为“myDiv”的div

实例:


所以我正在修改div上的文本,结果是文本从9变为0,而我认为应该从0变为9,因为递归
changeText(计数器)调用是在调用实际更改文本的方法之前进行的。

该函数包含一个异步超时

setTimeout(function(){
    recursiveCall(--counter);// calls the next function, which will call the next 
                             // and print in a timeout
    changeText(counter);  // print
},750);
在递归调用到达超时之前更改文本

如果愿意,可以将打印调用从超时之外移动,这将导致预期的行为:

function recursiveCall(counter){
    if(counter){
        recursiveCall(--counter);            
        setTimeout(function(){
            changeText(counter);
        },750);
    }
}
(尽管如此,请注意,这里的打印不是分开计时的,我们在某种程度上依赖于未定义的行为,假设它会先打印,因为我们将计时器放在第一位)

如果您希望它仍然延迟打印,您可以告诉函数它已经完成。递归最初仍将执行,但每个级别都会告诉其上的级别它已完成:

function recursiveCall(counter,done){
    if(counter){
        // note how recursion is done before the timeouts
        recursiveCall(counter-1,function(){ //note the function
            setTimeout(function(){          //When I'm done, change the text and let the 
                changeText(counter-1);      //next one know it's its turn.
                done(); // notify the next in line.
            },750);
        });
    }else{
        done(); //If I'm the end condition, start working.
    }
}

需要理解的一点是,它首先不是递归;如果您的函数没有正确的exit子句,那么这种情况可能会永远持续下去,而不会遇到崩溃的堆栈

原因是传递给
setTimeout()
的任何函数都在当前执行上下文之外运行;换句话说,代码“中断”了您的函数

如果您想要一个介于750ms之间的递归调用,可以执行如下操作:

function recursiveCall(counter, fn)
{
    if (counter) {
        recursiveCall(--counter, function() {
            changeText(counter);
            setTimeout(fn, 750);
        });
    } else if (fn) {
        fn(); // start chain backwards
    }
}
它在递归时创建一个回调链,exit子句设置整个回调链的运动,向后:)

严格来说,这里没有递归

setTimeout
的调用只是将回调添加到定时计时器事件列表中

大多数情况下,浏览器只是坐在那里等待事件,它会处理这些事件(即运行事件处理程序),然后返回等待事件

所以在这种情况下,你要做的是:

   recursiveCall(10)
   timer event and callback added to the queue
   function exits

... waits 750 ms ...

   timer event fires, callback pulled from the queue and invoked
      -> recursiveCall(9) invoked
        ->  timer event and callback added to the queue
      -> changeText(9) invoked
   callback function exits

... waits 750 ms ...

   timer event fires, callback pulled from the queue and invoked
      -> recursiveCall(8) invoked
        ->  timer event and callback added to the queue
      -> changeText(8) invoked
   callback function exits

and so on...

我称之为伪递归,因为尽管它看起来有点像经典递归,但每次调用都从同一个“堆栈帧”开始,也就是说,如果请求堆栈跟踪,通常只有一个(或者在您的情况下,有时是两个)
recursiveCall
的实例一次出现。

@anakata我想让你知道,你这么快就得到了答案(以及一次向上投票)是因为你的问题措辞非常好,其中包含了一个简单的可重复的例子。阅读手册的道具,很高兴我能帮上忙:)@elclanrs将附加参数传递到
setTimeout
是不允许的-standard@anakata另一方面,如果你想让它以另一种方式计算,你可以。我刚刚意识到,对于使用递归调用,加上setTimeout,你需要考虑三个层次,正常执行+调用的堆栈帧+异步calls@BenjaminGruenbaum:很好的信息,我想是IE阻碍了我们,真是个惊喜!也许它在“现代”IE中也能用。@anakata你是说
fn
参数是回调?是的,你可以这样称呼它:)我喜欢尾部递归吗?(但在单独的线程中?)严格地说,这是绝对递归:函数
recursiveCall
在自己的定义中引用自身。我想你的意思是,因为它从不调用自己,所以没有递归调用。@ruakh是的,虽然它引用自己,但它从不(直接)调用自己。这就是为什么我调用的是伪递归。@anakata不,不是真的-尾部递归是当对自身的递归调用是函数做的最后一件事时。这是一个很好的指标,表明可以用迭代实现代替函数。
   recursiveCall(10)
   timer event and callback added to the queue
   function exits

... waits 750 ms ...

   timer event fires, callback pulled from the queue and invoked
      -> recursiveCall(9) invoked
        ->  timer event and callback added to the queue
      -> changeText(9) invoked
   callback function exits

... waits 750 ms ...

   timer event fires, callback pulled from the queue and invoked
      -> recursiveCall(8) invoked
        ->  timer event and callback added to the queue
      -> changeText(8) invoked
   callback function exits

and so on...