Tec*_*ner 4 javascript recursion
我对理解"事件队列"和"调用堆栈"概念的好奇心在我解决这个问题时开始:
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
nextListItem();
}
};
Run Code Online (Sandbox Code Playgroud)
如果数组列表太大,以下递归代码将导致堆栈溢出.你如何解决这个问题仍然保留递归模式?
提到的解决方案是这样的:
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
setTimeout( nextListItem, 0);
}
};
Run Code Online (Sandbox Code Playgroud)
解:
由于事件循环处理递归而不是调用堆栈,因此消除了堆栈溢出.当nextListItem运行时,如果item不为null,则将超时函数(nextListItem)推送到事件队列并退出函数,从而使调用堆栈保持清零.当事件队列运行其超时事件时,将处理下一个项目并设置计时器以再次调用nextListItem.因此,在没有直接递归调用的情况下从开始到结束处理该方法,因此无论迭代次数如何,调用栈都保持清晰.
现在我的问题:
Q1)"事件队列"和"调用堆栈"之间有什么区别
Q2)我不明白答案.有人可以详细解释我吗?
Q3)当我在javascript中执行函数或调用变量或对象时.流程怎么样? 什么在调用堆栈?(比方说我做setTimeout ..它是去callstack还是事件队列?)
这些概念非常不清楚.我用谷歌搜索,但大多数结果不是我期望理解的.
请帮忙!
VLA*_*LAZ 15
事件队列和调用堆栈之间存在很大差异.事实上,它们几乎没有任何共同之处.
当你执行一个函数时,它所使用的所有内容都被称为堆栈,这就是你所指的那个调用堆栈.非常简化,它是功能执行的临时内存.或者换句话说
function foo() {
console.log("-> start [foo]");
console.log("<- end [foo]");
}
foo();Run Code Online (Sandbox Code Playgroud)
当被调用时,它将被赋予一个小沙盒来在堆栈上玩.当函数结束时,使用的临时内存将被擦除并可用于其他内容.因此,所使用的资源(除非在系统的某个地方给出)将只持续该函数持续的时间.
现在,如果你有嵌套函数
function foo() {
console.log("-> start [foo]");
console.log("<- end [foo]");
}
function bar() {
console.log("-> start [bar]");
foo()
console.log("<- end [bar]");
}
bar();Run Code Online (Sandbox Code Playgroud)
以下是调用函数时发生的情况:
bar 执行 - 为堆栈分配内存.bar 打印"开始"foo执行 - 为堆栈分配内存.NB! bar仍在运行,它的内存也存在.foo 打印"开始"foo 打印"结束"foo 完成执行并从堆栈中清除其内存.bar 打印"结束"bar 完成执行并从堆栈中清除其内存.因此,执行顺序是bar- > foo但是分辨率在最后,先出顺序(LIFO)foo结束 - > bar完成.
这就是它成为"堆叠"的原因.
这里需要注意的重要一点是,函数使用的资源只有在完成执行时才会释放.当它内部的所有函数和它们内部的函数完成执行时,它完成执行.所以,你可以像一个很深的调用堆栈a- > b- > c- > d- > e如果有任何大的资源,举办了a您需要b到e完成他们被释放之前.
在递归中,函数调用自身,它仍然在堆栈上创建条目.所以,如果a继续调用自己,你最终得到一个调用堆栈a- > a- > a- > a等.
这是一个非常简短的例子
// a very naive recursive count down function
function recursiveCountDown(count) {
//show that we started
console.log("-> start recursiveCountDown [" + count + "]");
if (count !== 0) {//exit condition
//take one off the count and recursively call again
recursiveCountDown(count -1);
console.log("<- end recursiveCountDown [" + count + "]"); // show where we stopped. This will terminate this stack but only after the line above finished executing;
} else {
console.log("<<<- it's the final recursiveCountDown! [" + count + "]"); // show where we stopped
}
}
console.log("--shallow call stack--")
recursiveCountDown(2);
console.log("--deep call stack--")
recursiveCountDown(10);Run Code Online (Sandbox Code Playgroud)
这是一个非常简单且非常有缺陷的递归函数,但它只用于演示在这种情况下会发生什么.
JavaScript在事件队列(或"事件循环")中运行,简单地说,它等待"活动"(事件),处理它们然后再次等待.
如果有多个事件,它将按顺序处理它们 - 先进先出(FIFO),因此是一个队列.那么,如果我们重新编写上述函数:
function foo() {
console.log("-> start [foo]");
console.log("<- end [foo]");
}
function bar() {
console.log("-> start [bar]");
console.log("<- end [bar]");
}
function baz() {
console.log("-> start [baz]");
setTimeout(foo, 0);
setTimeout(bar, 0);
console.log("<- end [baz]");
}
baz();Run Code Online (Sandbox Code Playgroud)
这是如何发挥作用.
baz被执行.在堆栈上分配的内存.foo 通过安排它运行"下一步"来延迟.bar 通过安排它运行"下一步"来延迟.baz饰面.堆栈被清除.foo.foo被执行.在堆栈上分配的内存.foo饰面.堆栈被清除.bar.bar被执行.在堆栈上分配的内存.bar饰面.堆栈被清除.正如你所希望看到的那样,堆栈仍在使用中.您调用的任何函数将始终生成堆栈条目.事件队列是一个单独的机制.
通过这种方式,您可以获得更少的内存开销,因为您不必使用任何其他功能来释放分配的资源.另一方面,您不能依赖任何完成的功能.
我希望这部分也能回答你的Q3.
如何推迟到队列帮助?
我希望上面的解释会更清楚,但它确保解释是有道理的:
堆栈的深度有一个限制.如果你考虑一下,它应该是显而易见的 - 只有大量的内存可以用来推测临时存储.达到最大调用深度后,JavaScript将抛出RangeError: Maximum call stack size exceeded错误.
如果你看一下recursiveCountDown上面给出的例子,很容易就会导致错误 - 如果你打电话,recursiveCountDown(100000)你会得到RangeError.
通过将所有其他执行放在队列中,您可以避免填满堆栈,从而避免使用RangeError.所以让我们重新编写这个函数
// still naive but a bit improved recursive count down function
function betterRecursiveCountDown(count) {
console.log("-> start recursiveCountDown [" + count + "]");
if (count !== 0) {
//setTimeout takes more than two parameters - anything after the second one will be passed to the function when it gets executed
setTimeout(betterRecursiveCountDown, 0, count - 1);
console.log("<- end recursiveCountDown [" + count + "]");
} else {
console.log("<<<- it's the final recursiveCountDown! [" + count + "]"); // show where we stopped
}
}
betterRecursiveCountDown(10);Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1183 次 |
| 最近记录: |