Как обработать необработанную часть ответа XMLHttpRequest без глобальных переменных?

Как в процессе JavaScript необработанный, но часть XMLHttpRequest responseText только в обработчике onprogress / onreadystatechange, без использования глобальных переменных для XMLHttpRequest, prevDataLength, nextreadPos и ​​inProgress (для блокировки)?

Проблема заключается в следующем: я хочу обновить веб-страницу на основе полученной в настоящее время части данных, полученных с использованием AJAX (текущее значение responseText XMLHttprequest). Я хочу обновить страницу, как только сервер отправит данные (это так называемый шаблон потоковой передачи HTTP). Когда я выполняю обновление, я хочу обработать только еще необработанную часть responseText.

Дополнительная сложность заключается в том, что responseText может содержать незаконченный фрагмент ответа; с этим легко справиться (как и я), используя терминаторы; тогда вы можете легко извлечь полные ответы.

В настоящее время я использую глобальные переменные и блокировку, чтобы избежать проблемы с вызовом обработчика, когда предыдущий вызов не завершился (обработка новых данных). Мой код выглядит (упрощенно) следующим образом:

function processData(unprocessed, nextReadPos) {
    var lastLineEnd = unprocessed.lastIndexOf('\n');
    if (lastLineEnd !== -1) {
        var lines = unprocessed.substring(0, lastLineEnd).split('\n');
        nextReadPos += lastLineEnd + 1 /* 1 == '\n'.length */;

        processLines(lines);
    } // end if

    return nextReadPos;
}

function handleResponse() {
    ...
    // here xhr.readyState === 4 && xhr.status === 200
    // in case we were called before finished processing
    if (inProgress) {
        return;
    } else {
        inProgress = true;
    }

    // extract new whole (complete) lines, and process them
    while (prevDataLength !== xhr.responseText.length) {
        if (xhr.readyState === 4 &&
            prevDataLength === xhr.responseText.length) {
            break;
        }

        prevDataLength = xhr.responseText.length;
        var unprocessed = xhr.responseText.substring(nextReadPos);
        nextReadPos = processData(unprocessed, nextReadPos);
    } // end while
    ...
    inProgress = false;
}
...
xhr.onreadystatechange = handleResponse;

Если бы у меня был более простой обработчик, я мог бы защитить от повторного ввода обработчика, когда он не завершил работу, передав объект XMLHttpRequest в качестве параметра, например, например случай 1 в разделе «Шаг 3 - простой пример» статьи AJAX / Getting_Started на Центр разработчиков Mozilla:

httpRequest.onreadystatechange = function() { alertContents(httpRequest); };  //1 (simultaneous request)

который в обозначениях, используемых в более крупном фрагменте кода, имеет вид

xhr.onreadystatechange = function() { handleResponse(xhr); };  //1 (simultaneous request)

Но этот пример не говорит мне, что делать с другими глобальными переменными: prevDataLength и nextReadPos. Они используются для определения наличия новых данных и для извлечения полных «предложений» из ответа сервера соответственно.

Как это сделать без глобальных переменных?


person Jakub Narębski    schedule 12.08.2009    source источник


Ответы (1)


Я отвечаю на свой вопрос, потому что других ответов не было

Во-первых, глобальная переменная inProgress, используемая в качестве семафора / мьютекса для защиты критического раздела, а цикл while (prevDataLength !== xhr.responseText.length) { ... } вообще не нужен.

Согласно ответу Уильяма на Нужны ли мьютексы в JavaScript? Вопрос StackOverflow:

Javascript определяется как реентерабельный язык, что означает, что пользователю не предоставляется доступ к потокам, в реализации могут быть потоки. Такие функции, как setTimeout() и асинхронные обратные вызовы, должны дождаться, пока обработчик сценариев спит, прежде чем они смогут работать.

Это согласуется с описанием в "Объектная модель документа ( DOM) События уровня 3 Спецификация W3C », глава 1.« События объектной модели документа », раздел 1.2« Отправка событий и поток событий DOM », последний абзац:

Модель событий DOM является реентерабельной. Слушатели событий могут выполнять действия, вызывающие отправку дополнительных событий. Такие события обрабатываются синхронно, распространение события, которое вызывает запуск прослушивателя событий, возобновится только после завершения отправки нового события.

Это означает, что все, что происходит в событии, должно быть завершено до того, как будет обработано следующее событие.


Во-вторых, глобальные переменные prevDataLength и nextReadPos могут (и, вероятно, должны) быть добавлены в качестве дополнительных дополнительных свойств к объекту XmlHttpRequest.

Альтернативным решением было бы использовать замыкания для частных / статических переменных, как это в некоторой степени описано в Частные члены в JavaScript Дугласа Крокфорда и в Пример 3: Инкапсуляция связанных функций в примечаниях к часто задаваемым вопросам comp.lang.javascript :: Замыкания Javascript.

person Jakub Narębski    schedule 18.08.2009