我可以使用setTimeout创建一个廉价的无限循环吗

Can I use setTimeout to create a cheap infinite loop?

本文关键字:一个 无限循环 可以使 setTimeout 创建 我可以      更新时间:2023-09-26
var recurse = function(steps, data, delay) {
    if(steps == 0) {
        console.log(data.length)
    } else {
        setTimeout(function(){
            recurse(steps - 1, data, delay);
        }, delay);
    }
};
var myData = "abc";
recurse(8000, myData, 1);

这段代码让我感到困扰的是,我传递了8000次字符串。这会导致任何类型的记忆问题吗?

此外,如果我用node.js运行这段代码,它会立即打印出来,这不是我所期望的。

如果你担心字符串被复制8000次,不要担心,字符串只有一个副本;传递的是一个参考。

更大的问题是,当你调用一个函数时创建的对象(称为"执行上下文"的"变量绑定对象"(是否会被保留,因为你正在创建一个闭包,并且它对上下文的变量对象有引用,因此只要闭包仍在某个地方被引用,它就会保留在内存中。

答案是:是的,但只有在计时器触发之前,因为一旦它不做任何事情,就不再引用闭包,因此垃圾收集器可以回收它们。所以你不会有8000个优秀的,只有一两个。当然,GC何时以及如何运行取决于实现。

奇怪的是,就在今天早些时候,我们就一个非常相似的话题提出了另一个问题;在那里也可以看到我的答案。

它会立即打印,因为程序会"立即"执行。根据time node test.js,在我的英特尔i5机器上,整个操作需要0.07s。

对于内存问题,以及这是否是一个"廉价的无限循环",你只需要进行实验和测量。

若要在节点中创建异步循环,可以使用process.nextTick。它将比setTimeout(func, 1)更快。

通常,Javascript不支持尾部调用优化,因此编写递归代码通常会有导致堆栈溢出的风险。如果像这样使用setTimeout,它可以有效地重置调用堆栈,因此堆栈溢出不再是问题。

不过,性能将是个问题,因为即使将delay设置为0,对setTimeout的每次调用通常也需要相当长的时间(约10ms(。

"1"是1毫秒。这可能是一个for循环。1秒是1000。我最近在后端写了一些类似的检查一批进程进度的内容,并设置了500的延迟。如果我没记错的话,旧的浏览器在1到15毫秒之间不会看到任何真正的区别。我认为V8实际上可能处理得比这更快。

我认为在上一次迭代完成之前,垃圾收集不会发生在任何函数上,但这些新一代的JS JIT编译器比我更了解的编译器聪明得多,所以他们可能会看到超时后什么都没有发生,并从内存中提取这些参数。

无论如何,即使为这些参数的每个实例保留了内存,也需要8000多次迭代才能产生问题。

使用内存密集型参数防止潜在问题的一种方法是,如果您传入一个具有所需参数的对象。那么我相信params将只是对记忆中某个固定位置的引用。

所以类似于:

var recurseParams ={ steps:8000, data:"abc", delay:100 }  //outside of the function
//define the function
recurse(recurseParams);
//Then inside the function reference like this:
recurseParams.steps--