【问题标题】:Prevent InternalError: too much recursion防止 InternalError:递归过多
【发布时间】:2017-01-07 23:30:45
【问题描述】:

我已经实现了一个递归执行的函数。因为它有时需要异步做事,所以不能使用简单的for/while循环,需要使用递归函数调用。

在一个非常抽象的 sn-p 中,这意味着以下内容:

function doStuff(){
   // async or sync things -> depends on several circumstances
   doStuff();
}
doStuff();

这很好用。但是——正如你所预料的——当超过最大递归调用限制时,这会导致问题。有时我需要处理超过 25,000 个调用,这会导致在最新的 Firefox (50.0a2) 中出现InternalError: too much recursion。

我发现捕获 InternalError 并使用超时重新触发回调是可行的:

function doStuff(){
    // async or sync things -> depends on several circumstances
    try{
        doStuff();
    } catch(e if e instanceof InternalError){
        setTimeout(function(){
            doStuff();
        }.bind(this), 25);
    }
}
doStuff();

但这看起来很老套和丑陋。因此,我问自己解决这种情况的首选方法是什么——当你不能在循环中处理事情并且需要使用递归函数调用时?

【问题讨论】:

  • 使用循环,因为当前浏览器中没有实现长尾调用消除。请注意,循环与尾递归一样具有表现力,具有自己的堆栈数据结构的循环与非尾递归一样具有表现力。不要使用setTimeout。
  • 1.为什么不使用setTimeout?我知道它很丑,但这有什么功能缺点? 2. 我不能使用正常循环,因为我有时需要运行异步代码。循环将继续运行...
  • setTimeout 非常慢。但是,我不明白你的伪代码。 doStuff 不接受任何参数并且不返回任何内容。递归情况是阻塞的。它的行为与正常循环完全相同。那么基本情况呢?
  • 在我的实际应用程序中,它需要多个参数并返回内容。基本情况是什么意思?

标签: javascript loops recursion callback


【解决方案1】:

将代码转换为异步。在这种情况下,您的调用堆栈不会很深

function doStuff(){
   setTimeout(doStuff, 0);
}
doStuff();

【讨论】:

  • 是的,这也是我发现的(见问题)。但这不是一个坏习惯吗?超时对我来说永远是警钟
  • 如果你必须在如此深的调用堆栈中使用递归,那么我只看到了到 asyc 的转换。它也不会阻止您的用户界面(但我认为它比同步示例慢)。为什么不能使用循环?
  • 好的,谢谢。让我们等几个小时以确保没有更好的方法。
  • 我可以确认这可行,但会使应用程序非常缓慢。我上面的方法只是在每次抛出递归错误后变慢。
  • 你可以实现简单的计数器,大约 10000 次递归调用,然后是异步调用。以防你不喜欢例外。
猜你喜欢
  • 1970-01-01
  • 2013-10-11
  • 1970-01-01
  • 2015-08-14
  • 2011-01-03
  • 1970-01-01
  • 2010-10-24
  • 1970-01-01
  • 2018-09-20
相关资源
最近更新 更多