在递归函数中处理大数组时堆栈溢出
Posted
技术标签:
【中文标题】在递归函数中处理大数组时堆栈溢出【英文标题】:Stack overflow while processing large array in recursive function 【发布时间】:2015-09-23 20:19:00 【问题描述】:如果数组列表太大,为什么下面的递归代码会导致堆栈溢出?我怎样才能解决这个问题并仍然保留递归模式?
var list = readHugeList();
var nextListItem = function()
var item = list.pop();
if (item)
// process the list item...
nextListItem();
;
【问题讨论】:
javascript 的调用堆栈大小非常有限。我相信当为 ES6 更新实现时这应该会改变,因为正确的尾调用是规范 IIRC 的一部分。要修复它,您需要以异步批处理的方式进行,但这会使您的代码需要回调。 @squint 另外,某些浏览器上的最大调用堆栈略高于 1400。Opera 12.17 及以下版本就是这种情况。一个解决方案是使用 1 毫秒的setTimeout
。
您可以在这里找到一些浏览器堆栈大小:***.com/questions/7826992/…
@IsmaelMiguel 我需要递归解决方案,正如您之前提到的,使用 setTimeout 有效,所以我接受了答案。
好吧。请考虑这次。我会记住这些事情的。我是 *** 的新手,所以我正在学习它的流程
【参考方案1】:
这听起来很奇怪,但请使用setTimeout
。
像这样:
//fill it with 50000 elements
var list = Array(50001).join('1.1').split('.');
var nextListItem = function()
var item = list.pop();
if (item) //should be list.length
// recursion here!
setTimeout( nextListItem, 0 );
;
nextListItem();
现在递归无限!
请注意,有些浏览器不喜欢那里的 0
。
作为副作用,您的代码不会阻止浏览器。
【讨论】:
所有浏览器都会在0
或任何虚假值上失败。由于您正在更改列表,因此您的基本情况应该是检查list.length
。此外,批量解决方案比为每个项目创建一个新的setTimeout
效率要高得多。它实际上从来都不是0
计时器,所以这可能需要相当长的时间。还有其他并发症,这取决于他实际在做什么。
@squint 你是对的。我只是展示了他可以做些什么来实现无限递归。是的,这需要相当长的时间(大约 50 秒),但代码是以非阻塞方式执行的,所以“有点”没问题。
我看到source article 也推荐使用setTimeout
作为堆栈溢出问题的解决方案。 不要使用setTimeout
来解决这个问题 – 有关更多详细信息,请阅读我对该主题的检查:***.com/a/43596323/633183
@naomik 很抱歉,您的考试没有说服我。此外,您的代码正在做上帝知道的事情,而这正在做一些更简单的事情。另外,您没有提到它如此慢的原因是浏览器等待至少 4 毫秒(以前是 10 毫秒),即使超时为 0 毫秒。事实上,我什至说它很慢(在上面的评论中)。虽然您的检查本身是有效的,但这并不重要:重要的是无限递归。为此,此答案以非阻塞方式工作得很好。
@naomik 然后随意添加您的答案,记住浏览器对 Promises、ES6 和所有爵士乐的支持状态。这是 2 年前提出的,从那时起发生了很多事情。事实上,没有指定环境。这意味着代码可以在 IE 5.5、Google Chrome 1.0、Netscape 或其他系统上运行。【参考方案2】:
似乎您只是在循环遍历一个数组。您是否尝试过使用简单的for
循环?
var list = readHugeList();
for (var i = 0; i < list.length; i++)
//Do something with list[i]
【讨论】:
这不会按照问题的要求保留递归。我确信他知道for
循环是如何工作的。
@squint 我同意你会期望几乎所有的前端 Web 开发人员都知道什么是简单的 for 循环,但是他的个人资料显示 ruby-on-rails 开发人员。不仅如此,我还遇到过很多开发人员经常尝试用复杂的代码来解决简单的问题,因为这正是他们大脑的工作方式。
可能是。然而,他特别想保留递归(这表明他知道命令式解决方案)。不说for
循环是不明智的。只是说它真的不能回答这个问题。坦率地说,有些问题用递归更容易解决。
@squint 好吧,希望 OP 会在此处回复 something 以提供任何一种指示。如果他们说“不,我不能这样做,原因是:X”,那么我将删除这个答案。
是的,我讨厌讨论时,除了提出问题的人之外的所有人都在场。【参考方案3】:
var taskList = breakBigTaskIntoMicroTasks(monsterTaskList);
// requestAnimationFrame will get executed in each 16ms of duration.
requestAnimationFrame(processTaskList);
function processTaskList(taskStartTime)
var taskFinishTime;
do
// Assume the next task is pushed onto a stack.
var nextTask = taskList.pop();
// Process nextTask.
processTask(nextTask);
// Go again if there’s enough time to do the next task.
taskFinishTime = window.performance.now();
while (taskFinishTime - taskStartTime < 3);
if (taskList.length > 0)
requestAnimationFrame(processTaskList);
【讨论】:
以上是关于在递归函数中处理大数组时堆栈溢出的主要内容,如果未能解决你的问题,请参考以下文章