xmlhttprequest timeout / abort не работает должным образом? - PullRequest
4 голосов
/ 25 января 2012

Вот моя функция AJAX:

/**
 * Send an AJAX request
 *
 * @param url      The URL to call (located in the /ajax/ directory)
 * @param data     The data to send (will be serialised with JSON)
 * @param callback The function to call with the response text
 * @param silent   If true, doesn't show errors to the user
 * @param loader   The element containing "Loading... Please wait"
 */
AJAX = function(url,data,callback,silent,loader) {
    var a,
        attempt = 0,
        rsc = function() {
            if( a.readyState == 4) {
                if( a.status != 200) {
                    if( a.status > 999) { // IE sometimes throws 12152
                        attempt++;
                        if( attempt < 5)
                            send();
                        else if( !silent) {
                            alert("HTTP Error "+a.status+" "+a.statusText+"<br />Failed to access "+url);
                        }
                    }
                    else if(!silent) {
                        alert("HTTP Error "+a.status+" "+a.statusText+"\nFailed to access "+url);
                    }
                }
                else {
                    callback(JSON.parse(a.responseText));
                }
            }
        },
        to = function() {
            a.abort();
            attempt++;
            if( attempt < 5)
                send();
            else if( !silent) {
                alert("Request Timeout\nFailed to access "+url);
            }
        };
    data = JSON.stringify(data);
    var send = function() {
        if( loader && attempt != 0) {
            loader.children[0].firstChild.nodeValue = "Error... retrying...";
            loader.children[1].firstChild.nodeValue = "Attempt "+(attempt+1)+" of 5";
        }
        a = new XMLHttpRequest();
        a.open("POST","/ajax/"+url,true);
        a.onreadystatechange = rsc;
        a.timeout = 5000;
        a.ontimeout = to;
        a.setRequestHeader("Content-Type","application/json");
        a.send(data);
    };
    send();
};

Общая идея - попытаться выполнить запрос до пяти раз.Иногда происходит сбой IE с необычной ошибкой HTTP (12xxx), а иногда сервер может не отвечать.

Проблема, с которой я столкнулся, заключается в том, что вызов abort() не прерывает соединение.Чтобы проверить, я сделал простой PHP-скрипт:

<?php
    sleep(60);
    touch("test/".uniqid());
    die("Request completed.");
?>

Вызов touch() создает файл с текущим uniqid() - просмотрев время модификации, я вижу время окончания sleep(60).

Ожидаемое поведение:

Запрос отправлен
Через пять секунд текст изменится на «Ошибка ... Повторная попытка ... Попытка 2/5»
Повторите вышеупомянутое до Попытки 5/5, затем потерпите неудачу.
Пять вызовов к файлу PHP прерваны, или либо будет пять файлов в папке «test», с интервалом в 5 секунд, либо будетнет, потому что ignore_user_abort выключен.

Наблюдаемое поведение (в IE9):

Запрос отправлен
Текст попытки появляется и изменяется, как и должен
После пяти попыток отображается сообщение об ошибке
Я не могу загрузить ни одной страницы в течение пяти полных минут.
На сервере пять файлов с интервалом в одну минуту

Донне знаю, что с этим делать, потому что на стороне сервера запросов 3,4 и 5 отправляются через несколько минут после появления в браузере сообщения об ошибке «Тайм-аут».

Если это имеет какое-либо значение, страница, выполняющая вызовы AJAX, находится в iframe.Перезагрузка iframe (использование iframe.contentWindow.location.reload() НЕ устраняет проблему, она все еще ожидает выполнения этих пяти запросов.

Почему это происходит? Как я могу это исправить?

РЕДАКТИРОВАТЬ: Я снова запустил тест, используя Инструменты разработчика для мониторинга сетевой активности. Результат:

URL          Method   Result     Type   Received  Taken   Initiator
/ajax/testto          (Aborted)              0 B  < 1 ms (Pending...)
/ajax/testto          (Aborted)              0 B  125 ms (Pending...)
/ajax/testto          (Aborted)              0 B  125 ms (Pending...)
/ajax/testto          (Aborted)              0 B  125 ms (Pending...)
/ajax/testto          (Aborted)              0 B  124 ms (Pending...)

Ответы [ 4 ]

4 голосов
/ 25 января 2012

Кажется, проблема в том, что timeout и ontimeout еще не являются частью некоторых реализаций:

var hasTimeout = 'timeout' in new XMLHttpRequest(); // false

По крайней мере, это не в Chrome 16 или Firefox7. И это должно вернуть true с учетом требований:

Атрибут timeout должен возвращать свое значение.Первоначально его значение должно быть равно нулю.

Оба были частью спецификации XHR 2 с 7 сентября 2010 .Но поскольку спецификация «Уровень 2» существует с 25 февраля 2008 г. и все еще является «Рабочим проектом», на самом деле нет гарантии, что любая реализация будет соответствоватьspec.


Если у вас нет доступных, вы можете попробовать вместо этого использовать onabort и setTimeout (как вы указали в своем комментарии):

// snip

to = function() {
    attempt++;
    if( attempt < 5)
        send();
    else if( !silent) {
        console.log("Request Timeout\nFailed to access "+url);
    }
};

// snip

var send = function() {
    if( loader && attempt != 0) {
        loader.children[0].firstChild.nodeValue = "Error... retrying...";
        loader.children[1].firstChild.nodeValue = "Attempt "+(attempt+1)+" of 5";
    }
    a = new XMLHttpRequest();
    a.open("POST","/ajax/"+url,true);
    a.onreadystatechange = rsc;
    setTimeout(function () {     /* vs. a.timeout */
        if (a.readyState < 4) {
            a.abort();
        }
    }, 5000);
    a.onabort = to;              /* vs. a.ontimeout */
    a.setRequestHeader("Content-Type","application/json");
    a.send(data);
    console.log('HTTP Requesting: %s', url);
};

// snip

Пример: http://jsfiddle.net/AmQGM/2/ - ?delay=2 должен закончиться, в то время как ?delay=10 истекает 5 попыток.

0 голосов
/ 30 сентября 2012

У меня была такая же проблема, она оказалась проблемой с сессией PHP. Если сеанс открыт, все остальные запросы из того же сеанса должны ждать.

<?php
    session_write_close();
    sleep(60);
    touch("test/".uniqid());
    die("Request completed.");
?>

Это, конечно, не помогло бы вам, если бы вы не начали сеанс:)

0 голосов
/ 02 февраля 2012

При запуске вашего кода я получил ошибку c00c023f.Когда я гуглил его, он придумал ответ:

http://www.enkeladress.com/article.php/internetexplorer9jscripterror

Это звучит очень похоже на то, что вы испытываете.

Вот такой вопрос, который имеетте же проблемы, с решением (также на основе вышеуказанной ссылки, но с дополнительной информацией): IE 9 Javascript error c00c023f

0 голосов
/ 27 января 2012

Прежде чем прервать запрос, попробуйте установить

a.onreadystatechange = function() {};

после этого, также добавьте проверку вокруг прерывания вызова:

if( a.readyState > 0 && a.readyState < 4 ) {
    a.abort();
}
...