假设我正在对一组值进行某种长时间的操作。
启动这个操作的函数是startNext()
最后一行执行的是它自己,所以它是一个像这样的递归调用:
function startNext(){
var val = getNextValue()
workOnValue(val)
.then(doSomeMoreWork)
.then(doMoreStuff)
.then(moree)
.then(startNext);
}
这将使堆栈增长,因为尾递归在 JS 中不起作用(目前)。 将最后一行更改为:
.then(function(){setTimeout(startNext, 0)});
工作更好? 它不会填满堆栈,因为它向事件循环添加了一个新操作吗?
最佳答案
是的,setTimeout
将阻止堆栈增长,因为当前函数完成并且对自身的“递归”调用被放置在事件队列中。它还会运行得更慢,因为它不是直接调用而是通过队列处理。
为了演示和证明这一点,请尝试使用 Node 进行实验。
将下面的代码示例放入一个文件中,并将 simple
标志切换到底部。您会看到 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 。为了尽可能简单,我在这里没有使用 promises。
关于javascript - 使用 setTimeout 会阻止堆栈增长吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33310551/