Каков самый чистый способ узнать о ходе выполнения запроса JQuery ajax?

105

В простом javascript все очень просто: нужно просто прикрепить обратный вызов к {XMLHTTPRequest}.onprogress

var xhr = new XMLHttpRequest();

xhr.onprogress = function(e){
    if (e.lengthComputable)
        var percent = (e.loaded / e.total) * 100;
};

xhr.open('GET', 'http://www...', true);
xhr.onreadystatechange = function() {
    ...
};
xhr.send(null);

но я делаю сайт ajax, который загружает данные html с помощью JQuery ( $.get()или $.ajax()), и мне было интересно, какой лучший способ получить ход выполнения запроса, чтобы отобразить его с небольшой полосой выполнения, но, что любопытно, я не найти что-нибудь полезное в документации JQuery ...

гуари
источник
4
Этот выглядит многообещающе dave-bond.com/blog/2010/01/JQuery-ajax-progress-HMTL5 для html5
PSL
1
Ох, спасибо, ребята! поэтому необходимо переопределить XHR .. странно то, что я осмотрен с Chrome Dev Tools так называемый jqXHRобъект (обертка объекта XHR возвращенного $.ajax()) и нашел progressатрибут в ней (наряду с abort, complete, successи т.д.), но в документах JQuery это отсутствует: api.jquery.com/jQuery.ajax/#jqXHR
guari
3
github.com/englercj/jquery-ajax-progress Я использую это, и он такой же, как и другие ответы, но я предпочитаю иметь более общий материал
KeizerBridge

Ответы:

138

Что-то вроде этого для $.ajax(только HTML5):

$.ajax({
    xhr: function() {
        var xhr = new window.XMLHttpRequest();
        xhr.upload.addEventListener("progress", function(evt) {
            if (evt.lengthComputable) {
                var percentComplete = evt.loaded / evt.total;
                //Do something with upload progress here
            }
       }, false);

       xhr.addEventListener("progress", function(evt) {
           if (evt.lengthComputable) {
               var percentComplete = evt.loaded / evt.total;
               //Do something with download progress
           }
       }, false);

       return xhr;
    },
    type: 'POST',
    url: "/",
    data: {},
    success: function(data){
        //Do something on success
    }
});
Мэттитоммо
источник
1
Выглядит многообещающе, но как это может работать? Весь конвейер состоит из трех шагов - отправка запроса, обработка запроса в бэкэнде для генерации некоторых данных и возврат их обратно. Каким образом клиентская сторона может узнать, что делается в бэкэнде и сколько времени потребуется, чтобы рассчитать прогресс?
SexyBeast
1
Заголовок ответа HTTP сообщает нам, сколько байтов ожидать, этот прогресс просто подсчитывает, сколько байтов было получено на данный момент. Он будет оставаться на нуле до тех пор, пока HTTP-ответ не будет отправлен,
Дж. Аллен,
2
Он работает только с POST, а не с GET и другими?
Raz
43

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

$.ajax(url)
  .progress(function(){
    /* do some actions */
  })
  .progressUpload(function(){
    /* do something on uploading */
  });

Проверьте это на github

likerRr
источник
Мне понравилось, как вы используете фабрику IFI. Я не знал этой техники!
CodeArtist
На данный момент это лучшее решение, предлагаемое здесь.
Atomless
2
Рабочее и элегантное решение, но вы можете знать, что оно может нарушить ваш существующий код, потому что оно нарушает все вызовы устаревших .success и .error. Он также удаляет все нестандартные атрибуты, которые вы устанавливаете для объекта jqXHR. Он также не предоставляет контекст в «this» для обратного вызова uploadProgress (может быть таким же для прогресса, но не тестировался), как это делается для всех стандартных обещаний для jqXHR. Таким образом, вам нужно будет передать контекст в закрытии.
откровенный
4
Я получаю сообщение об ошибке: TypeError: $.ajax(...).progress(...).progressUpload is not a function.... В чем проблема?
Universal Grasp
@UniversalGrasp привет, пожалуйста, откройте вопрос на github и предоставьте информацию о том, что вы сделали. Эта библиотека давно не обновлялась :) Возможно, что-то изменилось в самом jQuery
likerRr
5

Я пробовал три разных способа перехвата конструкции объекта Ajax:

  1. Моя первая попытка была использована xhrFields, но она позволяет использовать только одного слушателя, присоединяется только к прогрессу загрузки (не выгрузки) и требует того, что кажется ненужным копированием и вставкой.
  2. Моя вторая попытка прикрепила progressфункцию к возвращенному обещанию, но мне пришлось поддерживать свой собственный массив обработчиков. Мне не удалось найти хороший объект для прикрепления обработчиков, потому что в одном месте я имел бы доступ к XHR, а в другом - к jQuery XHR, но у меня никогда не было доступа к отложенному объекту (только его обещанию).
  3. Моя третья попытка дала мне прямой доступ к XHR для присоединения обработчиков, но снова потребовалось много кода копирования и вставки.
  4. Я завершил свою третью попытку и заменил jQuery ajaxсвоим собственным. Единственный потенциальный недостаток - вы больше не можете использовать свои собственные xhr()настройки. Вы можете учесть это, проверив, options.xhrявляется ли это функцией.

Я на самом деле вызываю свою promise.progressфункцию, xhrProgressчтобы потом легко ее найти. Вы можете назвать это как-нибудь иначе, чтобы разделить слушателей загрузки и скачивания. Надеюсь, это кому-то поможет, даже если исходный плакат уже получил то, что ему нужно.

(function extend_jQuery_ajax_with_progress( window, jQuery, undefined )
{
var $originalAjax = jQuery.ajax;
jQuery.ajax = function( url, options )
{
    if( typeof( url ) === 'object' )
    {options = url;url = undefined;}
    options = options || {};

    // Instantiate our own.
    var xmlHttpReq = $.ajaxSettings.xhr();
    // Make it use our own.
    options.xhr = function()
    {return( xmlHttpReq );};

    var $newDeferred = $.Deferred();
    var $oldPromise = $originalAjax( url, options )
    .done( function done_wrapper( response, text_status, jqXHR )
    {return( $newDeferred.resolveWith( this, arguments ));})
    .fail( function fail_wrapper( jqXHR, text_status, error )
    {return( $newDeferred.rejectWith( this, arguments ));})
    .progress( function progress_wrapper()
    {
        window.console.warn( "Whoa, jQuery started actually using deferred progress to report Ajax progress!" );
        return( $newDeferred.notifyWith( this, arguments ));
    });

    var $newPromise = $newDeferred.promise();
    // Extend our own.
    $newPromise.progress = function( handler )
    {
        xmlHttpReq.addEventListener( 'progress', function download_progress( evt )
        {
            //window.console.debug( "download_progress", evt );
            handler.apply( this, [evt]);
        }, false );
        xmlHttpReq.upload.addEventListener( 'progress', function upload_progress( evt )
        {
            //window.console.debug( "upload_progress", evt );
            handler.apply( this, [evt]);
        }, false );
        return( this );
    };
    return( $newPromise );
};
})( window, jQuery );
MarkMYoung
источник
Итак, я просто попытался реализовать ваше решение, но этот код для меня слишком профессиональный, чтобы понять - как мне его использовать? Я копирую вставленный весь ваш код перед своим document.ready и пробовал делать, $.ajax({ ... }).progress(function(evl) { console.log(evl); });но ничего не происходит. Вы можете помочь мне? :)
Патрик ДаВейдер
Какую версию jQuery вы используете?
Фло Шильд,
@FloSchild, пожалуйста, не редактируйте мой код только ради собственных предпочтений форматирования.
MarkMYoung
3

jQuery имеет AjaxSetup()функцию, которая позволяет вам регистрировать глобальные обработчики ajax, такие как beforeSendи completeдля всех вызовов ajax, а также позволяет вам получить доступ к xhrобъекту, чтобы выполнить прогресс, который вы ищете

Кевин Пей
источник
2
Спасибо за ссылку. Можете ли вы включить в свой ответ пример?
Майкл Шепер
$ .ajaxSetup ({xhr: function () {console.log ('настроить XHR ...');}});
Фло Шильд
7
И пример, который отвечает на вопрос? Боюсь, я не могу проголосовать за ответ, из-за которого мне приходится много возиться и читать, особенно когда на связанной странице ничего не говорится о прогрессе. Честно говоря, я скептически отношусь к этому, особенно учитывая предупреждение на этой странице, что говорит : «Примечание: функции обратного вызова Глобальные должны быть установлены с их соответствующим глобальным Ajax обработчика событий methods- .ajaxStart(), .ajaxStop(), .ajaxComplete(), .ajaxError(), .ajaxSuccess(), .ajaxSend()-rather чем в вариантах объекта для $.ajaxSetup(). ' < api.jquery.com/jQuery.ajaxSetup/#entry-longdesc >
Майкл Шепер
1
Из документации : установите значения по умолчанию для будущих запросов Ajax. Его использование не рекомендуется.
Ойвинд
-1

http://www.htmlgoodies.com/beyond/php/show-progress-report-for-long-running-php-scripts.html

Я искал подобное решение и нашел его полноценным.

var es;

function startTask() {
    es = new EventSource('yourphpfile.php');

//a message is received
es.addEventListener('message', function(e) {
    var result = JSON.parse( e.data );

    console.log(result.message);       

    if(e.lastEventId == 'CLOSE') {
        console.log('closed');
        es.close();
        var pBar = document.getElementById('progressor');
        pBar.value = pBar.max; //max out the progress bar
    }
    else {

        console.log(response); //your progress bar action
    }
});

es.addEventListener('error', function(e) {
    console.log('error');
    es.close();
});

}

и ваш сервер выводит

header('Content-Type: text/event-stream');
// recommended to prevent caching of event data.
header('Cache-Control: no-cache'); 

function send_message($id, $message, $progress) {
    $d = array('message' => $message , 'progress' => $progress); //prepare json

    echo "id: $id" . PHP_EOL;
    echo "data: " . json_encode($d) . PHP_EOL;
    echo PHP_EOL;

   ob_flush();
   flush();
}


//LONG RUNNING TASK
 for($i = 1; $i <= 10; $i++) {
    send_message($i, 'on iteration ' . $i . ' of 10' , $i*10); 

    sleep(1);
 }

send_message('CLOSE', 'Process complete');
Шри Наир
источник
Это показывает ход выполнения скрипта PHP, а не вызова AJAX.
Sinus Mackowaty
-3

Выполните следующие действия, чтобы отобразить ход выполнения запроса Ajax:

  1. Создайте Spinner с помощью HTML и CSS или используйте Bootstrap Spinner.
  2. Отображение счетчика, когда конечный пользователь запрашивает данные AJAX для бесконечного цикла или для порогового времени.
  3. Итак, после результата УСПЕХ / ОШИБКА запроса AJAX удалите счетчик, который в настоящее время отображается, и покажите свои результаты.

Чтобы упростить задачу, я рекомендую вам использовать классы JS для динамического отображения и скрытия счетчика для этой цели.

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

Рахул Гупта
источник
2
Это вовсе не «получение прогресса», только отображение анимации «ожидания».
Sinus Mackowaty