1

我在某些(但不是全部)IE7計算機上發生堆棧溢出錯誤。爲什麼通過XHR的異步onreadystatechange事件遞歸消耗棧?

該函數下載一堆基於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

做一個帶有計時器的遞歸出現防止堆棧溢出問題。

// 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中。我沒有看到其他瀏覽器的問題,所以我不能說。

0

你能控制檯日誌或提醒arguments.callee的值嗎?我很好奇,如果它解析爲preloadResources()函數的arguments變量而不是handleReadyStateChange(),會發生什麼情況。對我來說看起來不太可能,但它只是看你的代碼就跳到我身上。

在回答你的問題,但是 - 我想在上面的代碼中一個錯誤的做法是重新使用XMLHttpRequest對象,特別是沒有過讓生命週期已完成或調用xhr.abort()。這是一個禁忌,我回避了一段時間。它在這裏和網絡上的各個地方都有討論。請注意,IE重新使用xhr特別不適用。見http://ajaxian.com/archives/the-xmlhttprequest-reuse-dilemma

希望這有助於

斯科特

+0

感謝您的答覆,斯科特。正在評估'arguments.callee'。關於您引用的文章,原作者的作者寫了一篇[後續](http://radio.javaranch.com/pascarello/2006/03/31/1143817890773.html),稱xhr.abort()是不必要的你在調用'xhr.open()'後設置'onreadystatechange'的處理程序。 –