为什么通过XHR的异步onreadystatechange事件的递归消耗堆栈?

时间:2011-10-11 03:54:12

标签: javascript javascript-events stack-overflow xmlhttprequest

我在某些IE7计算机上遇到了堆栈溢出错误。

此功能可下载一堆基于URL的资源,并且不会对它们执行任何操作。它在我的登录页面上运行,其目的是在您输入凭据时获取静态内容,这样当您真正需要它时,浏览器就可以从其本地缓存中获取它。

// Takes an array of resources URLs and preloads them sequentially,
// but asynchronously, using an XHR object.
function preloadResources(resources) {

    // Kick it all off.
    if (resources.length > 0) {
        var xhr = getXHRObject(); // Prepare the XHR object which will be reused for each request.
        xhr.open('GET', resources.shift(), true);
        xhr.onreadystatechange = handleReadyStateChange;
        xhr.send(null);
    }

    // Handler for the XHR's onreadystatechange event.  Loads the next resource, if any.
    function handleReadyStateChange() {
        if (xhr.readyState == 4) {
            if (resources.length > 0) {
                xhr.open('GET', resources.shift(), true);
                xhr.onreadystatechange = arguments.callee;
                xhr.send(null);
            }
        }
    }

    // A safe cross-browser way to get an XHR object.
    function getXHRObject() {
        // Clipped for clarity.
    }

} // End preloadResources().

它被称为:

preloadResources([
    'http://example.com/big-image.png',
    'http://example.com/big-stylesheet.css',
    'http://example.com/big-script.js']);

它以递归方式处理一系列URL。我认为它不容易受到堆栈溢出错误的影响,因为每个递归都是从异步事件调用的 - XHR的onreadystatechange事件(注意我异步调用xhr.open())。我觉得这样做可以防止它堆积。

我不知道堆栈是如何失控的? 我哪里出错?

2 个答案:

答案 0 :(得分:2)

使用计时器执行递归可防止出现堆栈溢出问题。

// Handler for the XHR's onreadystatechange event.  Loads the next resource, if any.
function handleReadyStateChange() {
    if (xhr.readyState == 4 && resources.length > 0) {
        setTimeout(function() {
            xhr.open('GET', resources.shift(), true);
            xhr.onreadystatechange = handleReadyStateChange;
            xhr.send(null);
        }, 10);
    }
}

我猜彼此链接XHR请求会占用堆栈。将它们与计时器链接在一起可以防止这种情况 - 至少在IE7中。我没有在其他浏览器上看到这个问题,所以我不知道。

答案 1 :(得分:0)

您是否可以控制日志或提醒arguments.callee的值?我很好奇如果它从arguments函数解析为preloadResources()变量而不是handleReadyStateChange()会发生什么。对我来说似乎不太可能,但它突然出现在我身上,只是看着你的代码。

然而,在回答你的问题时 - 我认为上面代码中的一个不好的做法是重用XmlHttpRequest对象,特别是不要让生命周期完成或调用xhr.abort()。这是一个不,我一会儿就藏起来了。这里讨论了它和网络上的各个地方。请注意,IE特别适用于重用xhr。请参阅http://ajaxian.com/archives/the-xmlhttprequest-reuse-dilemma

希望这有帮助,

斯科特