Jaki jest najczystszy sposób uzyskania postępu żądania JQuery ajax?

105

W zwykłym javascript jest bardzo proste: wystarczy dołączyć wywołanie zwrotne do {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);

ale robię witrynę Ajax, która pobiera dane html za pomocą JQuery ( $.get()lub $.ajax()) i zastanawiałem się, jaki jest najlepszy sposób na uzyskanie postępu żądania, aby wyświetlić go z małym paskiem postępu, ale co ciekawe, nie jestem znajdowanie czegokolwiek przydatnego w dokumentacji JQuery ...

guari
źródło
4
Ten wygląda obiecująco dave-bond.com/blog/2010/01/JQuery-ajax-progress-HMTL5 dla html5
PSL
1
Och, dzięki chłopaki! więc potrzeba, aby zastąpić XHR .. dziwne jest to, że mam sprawdzone z Chrome Dev Tools tzw jqXHRobiekt (owijki obiektu zwróconego przez XHR $.ajax()) i znalazł progressatrybut w nim (wraz z abort, complete, success, itd.), ale w Dokumentach JQuery to brakuje: api.jquery.com/jQuery.ajax/#jqXHR
Guari
3
github.com/englercj/jquery-ajax-progress Używam tego i jest to to samo, co inne odpowiedzi, ale
wolę

Odpowiedzi:

138

Coś takiego dla $.ajax(tylko 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
    }
});
mattytommo
źródło
1
Wygląda obiecująco, ale jak to możliwe? Cały potok składa się z trzech kroków - wysłania żądania, przetworzenia żądania w zapleczu w celu wygenerowania niektórych danych i zwrócenia ich. Skąd klient może wiedzieć, co się dzieje w backendzie i ile czasu zajmie, zanim będzie mógł obliczyć postęp?
SexyBeast
1
Nagłówek odpowiedzi HTTP mówi nam, ile bajtów się spodziewać, ten postęp polega po prostu na zliczaniu, ile bajtów zostało dotychczas odebranych. Pozostanie na zero, dopóki odpowiedź HTTP nie zostanie faktycznie wysłana
J. Allen,
2
Czy działa tylko na POST, nie ma też GET i innych?
Raz
43

jQuery ma już zaimplementowane obietnice, więc lepiej korzystać z tej technologii i nie przenosić logiki zdarzeń do optionsparametru. Zrobiłem wtyczkę jQuery, która dodaje obietnicę postępu i teraz jest łatwa w użyciu, tak jak inne obietnice:

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

Sprawdź to na github

likerRr
źródło
Podobał mi się sposób, w jaki korzystasz z fabryki IFI. Nie znałem tej techniki!
CodeArtist,
Jest to obecnie najlepsze proponowane tutaj rozwiązanie.
bezatomowy
2
Działające i eleganckie rozwiązanie, ale możesz być świadomy, że może zepsuć istniejący kod, ponieważ przerywa wszystkie wywołania przestarzałych .success i .error. Usuwa również wszystkie niestandardowe atrybuty ustawione w obiekcie jqXHR. Nie dostarcza również kontekstu "this" dla wywołania zwrotnego uploadProgress (może taki sam dla postępu, ale nie testowany), jak jest to zrobione dla wszystkich standardowych obietnic dla jqXHR. Musisz więc podać kontekst w zamknięciu.
szczery
4
Pojawia się błąd: TypeError: $.ajax(...).progress(...).progressUpload is not a function.... w czym problem?
Universal Grasp
@UniversalGrasp Cześć, proszę, otwórz problem na github i podaj informacje o tym, co zrobiłeś. Ta biblioteka nie była aktualizowana od wieków :) może coś się zmieniło w samym jQuery
likerRr
5

Wypróbowałem trzy różne sposoby przechwycenia konstrukcji obiektu Ajax:

  1. Moja pierwsza próba została użyta xhrFields, ale pozwala tylko na jednego słuchacza, załącza tylko postęp pobierania (nie przesyłania) i wymaga czegoś, co wydaje się niepotrzebnym kopiowaniem i wklejaniem.
  2. Moja druga próba dołączyła progressfunkcję do zwróconej obietnicy, ale musiałem utrzymywać własną tablicę programów obsługi. Nie mogłem znaleźć dobrego obiektu do podłączenia modułów obsługi, ponieważ w jednym miejscu miałbym dostęp do XHR, a w innym do jQuery XHR, ale nigdy nie miałem dostępu do obiektu odroczonego (tylko jego obietnica).
  3. Moja trzecia próba dała mi bezpośredni dostęp do XHR w celu dołączenia modułów obsługi, ale znowu wymagało to dużej ilości kodu kopiowania i wklejania.
  4. Skończyłem trzecią próbę i zastąpiłem jQuery ajaxswoim własnym. Jedyną potencjalną wadą jest to, że nie możesz już używać własnego xhr()ustawienia. Możesz na to pozwolić, sprawdzając, czy options.xhrjest to funkcja.

W rzeczywistości nazywam swoją promise.progressfunkcję, xhrProgressaby móc ją później łatwo znaleźć. Możesz nadać mu inną nazwę, aby oddzielić odbiorniki przesyłania i pobierania. Mam nadzieję, że to pomoże komuś, nawet jeśli oryginalny plakat już zawierał to, czego potrzebował.

(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
źródło
Właśnie próbowałem zaimplementować Twoje rozwiązanie, ale ten kod jest dla mnie trochę za profesjonalny, abym mógł go zrozumieć - jak go używać? Skopiowałem, wkleiłem cały twój kod przed moim dokumentem. Już i próbowałem zrobić, $.ajax({ ... }).progress(function(evl) { console.log(evl); });ale nic się nie dzieje. Możesz mi pomóc? :)
Patrick DaVader
Której wersji jQuery używasz?
Flo Schild
@FloSchild, proszę nie edytować mojego kodu tylko ze względu na własne preferencje formatowania.
MarkMYoung
3

jQuery posiada AjaxSetup()funkcję, która pozwala na rejestrację globalnych programów obsługi AJAX, takich jak beforeSendi completedla wszystkich wywołań Ajax, a także umożliwia dostęp do xhrobiektu w celu wykonania żądanego postępu

Kevin Pei
źródło
2
Dzięki za link. Czy możesz podać przykład w swojej odpowiedzi?
Michael Scheper
$ .ajaxSetup ({xhr: function () {console.log ('setup XHR ...');}});
Flo Schild
7
A przykład, który odpowiada na to pytanie? Obawiam się, że nie mogę głosować za odpowiedzią, która powoduje, że muszę dużo majstrować i czytać, zwłaszcza gdy strona, do której prowadzi link, nie mówi nic o postępach. Szczerze mówiąc, jestem sceptyczny co do tego, zwłaszcza biorąc pod uwagę ostrzeżenie na tej stronie, to mówi „Uwaga: Globalne funkcje zwrotne powinny być ustawione z ich odpowiednimi obsługi zdarzeń globalnych Ajax METODY .ajaxStart(), .ajaxStop(), .ajaxComplete(), .ajaxError(), .ajaxSuccess(), .ajaxSend()-rather niż w ramach opcji dla obiektów $.ajaxSetup()”. < api.jquery.com/jQuery.ajaxSetup/#entry-longdesc >
Michael Scheper
1
Z dokumentacji : Ustaw wartości domyślne dla przyszłych żądań Ajax. Nie zaleca się jego stosowania.
Oyvind
-1

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

Szukałem podobnego rozwiązania i znalazłem to w pełni.

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();
});

}

i wyjścia serwera

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');
Sri Nair
źródło
Pokazuje postęp uruchomionego skryptu PHP, a nie wywołania AJAX.
Sinus Mackowaty
-3

Postępuj zgodnie z instrukcjami, aby wyświetlić postęp żądania Ajax:

  1. Utwórz Spinner za pomocą Html i CSS lub użyj Bootstrap Spinner.
  2. Wyświetlaj Spinner, gdy użytkownik końcowy żąda danych AJAX dla nieskończonej pętli lub progu limitu czasu.
  3. Tak więc, po SUCCESS / ERROR wyniku żądania AJAX, usuń Spinner, który jest aktualnie wyświetlany i pokaż swoje wyniki.

Aby to ułatwić, polecam używanie klas JS do dynamicznego wyświetlania i ukrywania pokrętła w tym celu.

Mam nadzieję, że to pomoże!

Rahul Gupta
źródło
2
To wcale nie jest „uzyskiwanie postępu”, a jedynie pokazanie animacji „oczekiwania”.
Sinus Mackowaty