我正在实现长轮询系统的客户端。我读到的所有内容都表明,为了避免无限的堆栈深度,不要进行直接递归调用,而是使用
setTimeout()
间接调用,以便每次迭代都以干净的堆栈开始。
实现是:
function pollForEvents() {
$.get(
"GetEventLP",
null,
function(eventData) {
console.log("Got event at "+new Date()+": "+JSON.stringify(eventData));
setTimeout(function() {pollForEvents();}, 1); // Using setTimeout() avoids infinite recursion stack growth
}).fail(function(xhr, status, err) {
console.log("Long poll failed at "+new Date()+": "+util.getServerErrorText(xhr));
// Wait before we try again to avoid fast failure polling
setTimeout(function() { pollForEvents(); }, 60000);
});
}
我让它运行一定次数的迭代,然后在
$.get()
调用处设置一个断点。在下一次迭代中,它会中断,Chrome 会显示与运行的迭代次数一样深的调用堆栈 - 例如它会无限地增长。我误解了什么吗?这看起来像是基于不断增加的调用堆栈大小的内存泄漏。
这是 Chrome 在 6 次迭代后显示的调用堆栈,请注意重复的
pollForEvents()
帧。
pollForEvents (index.js?v=1.02:406)
(anonymous) (index.js?v=1.02:411)
setTimeout (async)
(anonymous) (index.js?v=1.02:411)
i (jquery-3.1.1.min.js:2)
fireWith (jquery-3.1.1.min.js:2)
A (jquery-3.1.1.min.js:4)
(anonymous) (jquery-3.1.1.min.js:4)
load (async)
send (jquery-3.1.1.min.js:4)
ajax (jquery-3.1.1.min.js:4)
r.<computed> (jquery-3.1.1.min.js:4)
pollForEvents (index.js?v=1.02:406)
(anonymous) (index.js?v=1.02:411)
setTimeout (async)
(anonymous) (index.js?v=1.02:411)
i (jquery-3.1.1.min.js:2)
fireWith (jquery-3.1.1.min.js:2)
A (jquery-3.1.1.min.js:4)
(anonymous) (jquery-3.1.1.min.js:4)
load (async)
send (jquery-3.1.1.min.js:4)
ajax (jquery-3.1.1.min.js:4)
r.<computed> (jquery-3.1.1.min.js:4)
pollForEvents (index.js?v=1.02:406)
(anonymous) (index.js?v=1.02:411)
setTimeout (async)
(anonymous) (index.js?v=1.02:411)
i (jquery-3.1.1.min.js:2)
fireWith (jquery-3.1.1.min.js:2)
A (jquery-3.1.1.min.js:4)
(anonymous) (jquery-3.1.1.min.js:4)
load (async)
send (jquery-3.1.1.min.js:4)
ajax (jquery-3.1.1.min.js:4)
r.<computed> (jquery-3.1.1.min.js:4)
pollForEvents (index.js?v=1.02:406)
(anonymous) (index.js?v=1.02:411)
setTimeout (async)
(anonymous) (index.js?v=1.02:411)
i (jquery-3.1.1.min.js:2)
fireWith (jquery-3.1.1.min.js:2)
A (jquery-3.1.1.min.js:4)
(anonymous) (jquery-3.1.1.min.js:4)
load (async)
send (jquery-3.1.1.min.js:4)
ajax (jquery-3.1.1.min.js:4)
r.<computed> (jquery-3.1.1.min.js:4)
pollForEvents (index.js?v=1.02:406)
(anonymous) (index.js?v=1.02:411)
setTimeout (async)
(anonymous) (index.js?v=1.02:411)
i (jquery-3.1.1.min.js:2)
fireWith (jquery-3.1.1.min.js:2)
A (jquery-3.1.1.min.js:4)
(anonymous) (jquery-3.1.1.min.js:4)
load (async)
send (jquery-3.1.1.min.js:4)
ajax (jquery-3.1.1.min.js:4)
r.<computed> (jquery-3.1.1.min.js:4)
pollForEvents (index.js?v=1.02:406)
(anonymous) (index.js?v=1.02:411)
setTimeout (async)
(anonymous) (index.js?v=1.02:411)
i (jquery-3.1.1.min.js:2)
fireWith (jquery-3.1.1.min.js:2)
A (jquery-3.1.1.min.js:4)
(anonymous) (jquery-3.1.1.min.js:4)
我发现这里发生了什么,如果其他人开始对像这样的明显的调用堆栈内存泄漏感到恐慌,也许这很有用。这不是内存泄漏,但原因如下:
默认的 Chrome 开发人员调用堆栈视图启用了“异步堆栈跟踪”,这似乎在涉及异步函数时创建了堆栈的人工视图(https://developer.chrome.com/docs/devtools/javascript/reference /#调用堆栈)。它实际上是过去调用堆栈的视图,它不仅仅显示当前堆栈。
启用此功能后,调试器将保留先前的异步调用堆栈并在此视图中显示所有内容。堆栈之间画有一条细线,但这意味着什么并不明显。此视图对于调试异步调用链非常方便,但如果您认为正在查看一组非常深的嵌套调用,则可能具有欺骗性。禁用此功能后,堆栈仅包含预期的一组帧 - 没有调用堆栈内存泄漏。