有人问我(由朋友)建立一个计时器(每秒写一行的无限计时器),但没有setInterval
。
我solved与:
var i = 0;
function k(myId, cb)
{
setTimeout(function ()
{
console.log(myId);
cb();
}, 1000);
}
function go()
{
i++;
k(i, go);
}
go();
它正在发挥作用。
问题在于我担心会有内存压力。它实际上创建了一个递归,并在一段时间后(一周或某事) - 该过程将消耗大量内存。 (堆栈永远不会被释放)
如何更改代码以免耗费大量内存?
答案 0 :(得分:13)
它可能看起来像递归,但是setTimeout不会创建递归。
setTimeout的工作方式是它立即返回。因此,对k
的调用会在其堆栈被释放后立即结束。
当超时实际发生且对go
的调用再次发生时,它不是从前一次调用k
开始,而是来自全局范围*。
*注意:我没有使用ECMAScript规范中定义的范围的严格含义。我的意思是对k
的调用就好像你用普通的<script></script>
标签写的那样:也就是说,在任何其他函数调用之外。
在您的特定情况下,k
函数创建的闭包中实际包含的内容很少。唯一重要的闭包是对参数cb
和myId
的引用。即便如此,它只持续大约一秒钟:
#1 function k(myId, cb) {
#2 setTimeout(function(){
#3 console.log(myId); // there is a closure here to myId
#4 cb(); // and another one for cb
#5
/* But at this point in the function, setTimeout ends
* and as the function returns, there are no remaining
* references to either "cb" or "myId" accessible
* anywhere else. Which means that the GC can immediately
* free them (though in reality the GC may run a bit later)
*/
#6 }, 1000); // So one second is roughly the longest the closure lasts
}
我应该注意到你的代码相当复杂。它可以写得更简单,如果你只是这样写的话,根本不使用闭包(减去全局变量i):
// Simpler, does exactly the same thing:
var i = 0;
function go () {
console.log(i);
i++;
setTimeout(go, 1000); // callback
}
go();
答案 1 :(得分:6)
这一行是假的:
它实际上创建了一个递归,经过一段时间(一周或某事) - 该过程将消耗大量内存。 (堆栈永远不会被释放)
不创建递归,因为该函数完全退出 ,然后再次调用。
递归堆叠在彼此之上
function a() {a()}; // function calls itself until a stack overflow.
堆栈看起来像这样
a()
a()
a()
a() ... until a crash.
使用setTimeout,您可以执行一个函数。该函数设置一个事件让函数再次运行 - 但这里有一个重要的区别:函数退出,完全,并且已经消失[1]。然后它再次被召唤。
执行明智,与此相比没有太大区别:
function a() {console.log("I am called");}
a(); // Call the function;
a(); // Call the function again
a(); // Call the function again
setTimeout
只是让浏览器有机会“呼吸”,如果你愿意的话。屏幕更新的机会,其他事件要处理。使用正确的术语,block
浏览器不会。
答案 2 :(得分:2)
这不会造成内存泄漏。
事实上,这是一个非常常用的概念。通常它以这种形式出现:
setTimeout(function next() {
// Do something...
// Have the function set another timeout to call itself later.
setTimeout(next, 10);
}, 10);
如果要检查某些内容(此处每10毫秒),最好使用此模式而不是setInterval
,因为它可以提高页面性能。例如,如果您的函数执行时间超过10毫秒,并且您使用setInterval(f, 10)
则会不断调用它。但是,如果您使用上面的setTimeout
模式,那么无论您的函数执行多长时间,它都至少会确保处理器在每次调用之间间隔10毫秒。
有关此模式的详细信息,请参阅this video (starting at 7:46) from Paul Irish。