Який найчистіший спосіб досягти прогресу запиту 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

Відповіді:


139

Щось подібне для $.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 та інші?
Раз

43

jQuery вже реалізував обіцянки, тому краще використовувати цю технологію, а не переміщувати логіку подій до optionsпараметра. Я створив плагін jQuery, який додає обіцянку прогрес, і тепер його легко використовувати так само, як і інші обіцянки:

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

Перевірте це в github


Мені сподобалось те, як ти використовуєш фабрику IFI. Я не знав цієї техніки!
CodeArtist

Наразі це найкраще рішення, запропоноване тут.
атомів

2
Робоче та елегантне рішення, але ви можете знати, що він може порушити ваш існуючий код, оскільки він порушує всі виклики застарілих .success та .error. Він також знімає всі нестандартні атрибути, встановлені на об'єкт jqXHR. Він також не надає контексту в "це" для зворотного виклику uploadProgress (можливо, той самий для прогресу, але не перевірений), як це робиться для всіх стандартних обіцянок jqXHR. Тож вам потрібно буде пройти контекст у закритті.
відвертий

4
Я отримую помилку: TypeError: $.ajax(...).progress(...).progressUpload is not a function.... У чому проблема?
Універсальне схоплення

@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 );

Тому я просто спробував реалізувати ваше рішення, але цей код трохи занадто професійний для мене, щоб зрозуміти - як це використовувати? Я копіюю вставлений весь код перед моїм документом. Вже намагався зробити, $.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.
Синус Макковаті

-3

Виконайте кроки, щоб відобразити хід запиту на Ajax:

  1. Створіть Spinner за допомогою Html & CSS або використовуйте Sputner Bootstrap.
  2. Відобразить Spinner, коли кінцевий користувач запитує дані AJAX для нескінченного циклу або для граничного граничного часу.
  3. Отже, після результату ЗАВДАННЯ / ПОМИЛКИ запиту AJAX, видаліть Spinner, який наразі відображається, та покажіть свої результати.

Для зручності я рекомендую для цього використовувати класи JS для динамічного відображення та приховування прядильника.

Сподіваюся, це допомагає!


2
Це зовсім не "досягнення прогресу", а лише показ "очікування" анімації.
Sinus Mackowaty
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.