使用setTimeout会阻止堆栈增长吗?
作者:互联网
可以说我正在为一组值进行某种长时间的操作.
启动此操作的函数是startNext()
并且其中执行的最后一行本身就是它本身,因此它是一个递归调用,如下所示:
function startNext(){
var val = getNextValue()
workOnValue(val)
.then(doSomeMoreWork)
.then(doMoreStuff)
.then(moree)
.then(startNext);
}
这将使堆栈增长,因为尾递归在JS中尚不起作用(尚未).
将最后一行更改为:
.then(function(){setTimeout(startNext, 0)});
工作更好?
它不会填充堆栈,因为它向事件循环添加了新操作吗?
解决方法:
是的,setTimeout将阻止堆栈增长,因为当前函数已完成,并且对其自身的“递归”调用被放置在事件队列中.由于它不会被直接调用而是通过队列进行处理,因此它的运行速度也会变慢.
为了证明这一点,请尝试使用Node进行实验.
将下面的代码示例放入文件中,然后将简单标志切换到底部.您将看到recurseSimple函数超级快地运行,并且非常快速地释放堆栈. recurseTimeout运行较慢,但将永远运行.
function recurseSimple(count) {
// Count: 15269, error: bootstrap_node.js:392
// RangeError: Maximum call stack size exceeded
try {
if (count % 10000 === 0) {
console.log('Running count:', count);
}
recurseSimple(count + 1);
} catch (e) {
console.log(`Simple count: ${count}, error:`, e);
}
}
function recurseTimeout(count) {
// No stack exceeded
try {
if (count % 10000 === 0) {
console.log('Running count:', count);
}
setTimeout(recurseTimeout.bind(null, count + 1), 0);
} catch (e) {
console.log(`Timeout count: ${count}, error:`, e);
}
}
const simple = false;
if (simple) {
recurseSimple(0);
} else {
recurseTimeout(0);
}
完全相同的原则适用于承诺.我在这里没有使用Promise来使其尽可能简单.
标签:performance,stack-overflow,javascript 来源: https://codeday.me/bug/20191119/2036941.html