Почему рекурсия через асинхронное событие onreadystatechange XHR потребляет стек? - PullRequest
1 голос
/ 11 октября 2011

Я получаю ошибку переполнения стека на некоторых, но не на всех машинах 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-адресов. Я думал, что он не подвержен ошибкам переполнения стека, потому что каждая рекурсия вызывается из асинхронного события - события onreadystatechange XHR (обратите внимание, что я вызываю xhr.open() асинхронно). Я чувствовал, что это предотвратит рост стека.

Я не вижу, как стек выходит из-под контроля? Где я ошибся?

Ответы [ 2 ]

2 голосов
/ 02 ноября 2011

Выполнение рекурсии с таймером предотвратило появление проблемы переполнения стека.

// 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 голосов
/ 11 октября 2011

Можете ли вы утешить журнал или предупредить значение arguments.callee?Мне любопытно, что произойдет, если она разрешится в переменную arguments из функции preloadResources() вместо handleReadyStateChange().Мне это кажется маловероятным, но это бросается в глаза, просто глядя на ваш код.

Однако, в ответ на ваш вопрос - я думаю, что одной из плохих практик в приведенном выше коде является повторное использование объекта XmlHttpRequest, особеннопозволяя завершить жизненный цикл или позвонив по номеру xhr.abort().Это нет-нет, я спрятался некоторое время назад.Это обсуждается здесь и в разных местах в Интернете.Обратите внимание, что IE особенно плохо работает с повторным использованием xhr.См. http://ajaxian.com/archives/the-xmlhttprequest-reuse-dilemma.

Надеюсь, это поможет,

Скотт

...