【发布时间】:2015-11-27 22:25:12
【问题描述】:
无法理解一件事。在服务器上,我有一些进程在异步模式下永远运行。比如这样:
function loginf() {
console.log(1+1);
process.nextTick(loginf);
}
loginf();
它的恢复,据我所知,它必须导致堆栈溢出和(或)吃掉内存。 如何在node.js中长时间运行而不会发生内存泄漏?有可能吗?
【问题讨论】:
无法理解一件事。在服务器上,我有一些进程在异步模式下永远运行。比如这样:
function loginf() {
console.log(1+1);
process.nextTick(loginf);
}
loginf();
它的恢复,据我所知,它必须导致堆栈溢出和(或)吃掉内存。 如何在node.js中长时间运行而不会发生内存泄漏?有可能吗?
【问题讨论】:
如果您想重复执行某项操作,并且希望以一种友好的方式执行此操作,从而为服务器中的其他事件留出处理周期,那么通常的方法是使用setInterval()。
setInterval(function() {
console.log("called");
}, 1000);
像使用process.nextTick() 一样重复调用相同的函数并不是真正的递归,也不会导致堆栈溢出,因为堆栈在事件队列下一次调用该函数之前完全展开。它完成当前的执行路径,然后调用你传递给nextTick()的函数。
您对此类操作的选择是:
setInterval()
setTimeout()
setImmediate()
process.nextTick()
所有三个选项都让当前执行线程在调用回调函数之前完成,因此不会堆积堆栈。
setInterval() 使用系统计时器设置为将来的某个时间,并允许在调用setInterval() 回调之前处理当前在队列中或在计时器时间发生之前队列中的所有其他事件。当回调调用之间的时间暂停是可取的并且您希望一遍又一遍地重复调用回调时,请使用setInterval()。
setTimeout() 使用系统计时器设置为将来的某个时间,并允许在调用setTimeout() 回调之前处理当前在队列中或在计时器时间发生之前队列中的所有其他事件。您可以重复使用setTimeout()(为每个回调设置另一个超时),尽管这通常是setInterval() 的设计目的。 node.js 中的setTimeout() 不遵循浏览器执行的最小时间间隔,因此setTimeout(fn, 1) 将很快被调用,但由于实现差异,不如setImmediate() 或process.nextTick() 快。
setImmediate() 在当前位于事件队列中的其他事件全部得到服务后立即运行。因此,这对系统中的其他事件是“公平的”。请注意,这比setTimeout(fn, 0); 更有效,因为它不需要使用系统计时器,而是直接编码到事件子系统中。当您希望堆栈展开并且希望首先处理队列中的其他事件时使用此选项,但您希望回调尽快运行。
process.nextTick() 在当前执行线程完成(并且堆栈展开)后立即运行,但在当前事件队列中的任何其他事件之前运行。这不“公平”,如果您一遍又一遍地使用process.nextTick() 运行某些东西,您将让系统无法处理其他类型的事件。它可以使用一次,以便在堆栈展开后尽快运行某些东西,但不应一遍又一遍地重复使用。
一些有用的参考资料:
Does Node.js enforce a minimum delay for setTimeout?
【讨论】:
.nextTick() 也不会添加到堆栈中,但它不会像 setInterval() 那样为其他事件运行留下尽可能多的机会,并且会占用 CPU。 setImmediate() 是另一种选择。有关这方面的信息,请参阅 stackoverflow.com/questions/15349733/setimmediate-vs-nexttick。
setInterval()、setImmediate() 和 process.nextTick() 让当前执行线程在执行回调之前完成,这样就不会堆积堆栈。