Was ist der sauberste Weg, um den Fortschritt der JQuery Ajax-Anfrage zu erhalten?

103

Im Klartext ist Javascript sehr einfach: Sie müssen nur den Rückruf anhängen {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);

Aber ich mache eine Ajax-Site, die HTML-Daten mit JQuery ( $.get()oder $.ajax()) herunterlädt, und ich habe mich gefragt, wie ich den Fortschritt einer Anfrage am besten abrufen kann, um sie mit einem kleinen Fortschrittsbalken anzuzeigen, aber seltsamerweise nicht in der JQuery-Dokumentation etwas Nützliches finden ...

Guari
quelle
4
Dieser sieht vielversprechend aus dave-bond.com/blog/2010/01/JQuery-ajax-progress-HMTL5 für HTML5
PSL
1
Ooh danke Jungs! so müssen außer Kraft zu setzen xhr .. das Merkwürdige ist , dass ich mit Chrome Dev Tools , um die so genannte inspiziert haben jqXHRObjekt (der Wrapper von XHR - Objekt zurückgegeben durch $.ajax()) und fand ein progressAttribut in sie (zusammen mit abort, complete, success, etc.), aber In JQuery-Dokumenten fehlt dies: api.jquery.com/jQuery.ajax/#jqXHR
Guari
3
github.com/englercj/jquery-ajax-progress Ich benutze dies und es ist ziemlich das gleiche wie andere Antworten, aber ich bevorzuge allgemeinere Sachen
KeiserBridge

Antworten:

138

So etwas für $.ajax(allerdings nur 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
quelle
1
Sieht vielversprechend aus, aber wie kann das möglicherweise funktionieren? Die gesamte Pipeline besteht aus drei Schritten: Senden einer Anforderung, Verarbeiten der Anforderung im Backend, um einige Daten zu generieren, und Zurückgeben. Wie kann die Client-Seite möglicherweise wissen, was im Backend getan wird und wie lange es dauern wird, bis der Fortschritt berechnet werden kann?
SexyBeast
1
Der HTTP-Antwortheader gibt an, wie viele Bytes zu erwarten sind. Bei diesem Fortschritt wird lediglich gezählt, wie viele Bytes bisher empfangen wurden. Es bleibt bei Null, bis die HTTP-Antwort tatsächlich gesendet wird
J. Allen
2
Funktioniert es nur bei POST, nicht auch bei GET und den anderen?
Raz
43

jQuery hat bereits Versprechen implementiert, daher ist es besser, diese Technologie zu verwenden und die Ereignislogik nicht in optionsParameter zu verschieben. Ich habe ein jQuery-Plugin erstellt, das Fortschrittsversprechen hinzufügt, und jetzt ist es einfach zu verwenden, genau wie andere Versprechen:

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

Schau es dir bei Github an

likerRr
quelle
Mir hat gefallen, wie Sie die IFI-Fabrik nutzen. Ich kannte diese Technik nicht!
CodeArtist
Dies ist derzeit die beste hier vorgeschlagene Lösung.
Atomlos
2
Funktionierende und elegante Lösung, aber Sie sind sich möglicherweise bewusst, dass Ihr vorhandener Code beschädigt werden kann, da alle Aufrufe des veralteten .success und .error unterbrochen werden. Außerdem werden alle nicht standardmäßigen Attribute entfernt, die Sie für ein jqXHR-Objekt festgelegt haben. Es bietet auch nicht den Kontext zu "this" für den uploadProgress-Rückruf (möglicherweise derselbe für den Fortschritt, aber nicht getestet), da dies für alle Standardversprechen für jqXHR erfolgt. Sie müssen den Kontext also in einem Abschluss übergeben.
Frank
4
Ich erhalte die Fehlermeldung: TypeError: $.ajax(...).progress(...).progressUpload is not a function.... Was ist das Problem?
Universal Grasp
@UniversalGrasp Hallo, bitte öffnen Sie ein Problem bei github und geben Sie Informationen darüber, was Sie getan haben. Diese Bibliothek wurde seit Ewigkeiten nicht mehr aktualisiert :) Möglicherweise hat sich in jQuery selbst etwas geändert
likerRr
5

Ich habe drei verschiedene Möglichkeiten ausprobiert, um die Konstruktion des Ajax-Objekts abzufangen:

  1. Mein erster Versuch verwendet xhrFields, aber das erlaubt nur einen Listener, hängt nur an den Download-Fortschritt (nicht an den Upload) und erfordert scheinbar unnötiges Kopieren und Einfügen.
  2. Mein zweiter Versuch fügte progressdem zurückgegebenen Versprechen eine Funktion hinzu, aber ich musste meine eigene Reihe von Handlern warten. Ich konnte kein gutes Objekt zum Anhängen der Handler finden, da ich an einem Ort auf das XHR und an einem anderen auf das jQuery XHR zugreifen würde, aber ich hatte nie Zugriff auf das zurückgestellte Objekt (nur dessen Versprechen).
  3. Bei meinem dritten Versuch hatte ich direkten Zugriff auf das XHR zum Anhängen von Handlern, musste aber erneut viel Code kopieren und einfügen.
  4. Ich habe meinen dritten Versuch abgeschlossen und jQuery's ajaxdurch meinen eigenen ersetzt. Das einzige mögliche Manko ist, dass Sie Ihre eigene xhr()Einstellung nicht mehr verwenden können . Sie können dies berücksichtigen, indem Sie überprüfen, ob options.xhres sich um eine Funktion handelt.

Ich rufe meine promise.progressFunktion tatsächlich auf, xhrProgressdamit ich sie später leicht finden kann. Vielleicht möchten Sie es anders benennen, um Ihre Upload- und Download-Listener zu trennen. Ich hoffe, das hilft jemandem, auch wenn das Originalplakat bereits das bekommen hat, was er brauchte.

(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
quelle
Ich habe gerade versucht, Ihre Lösung zu implementieren, aber dieser Code ist ein bisschen zu professionell, als dass ich ihn verstehen könnte - wie verwende ich ihn? Ich habe Ihren gesamten Code vor meinem Dokument kopiert und bereits versucht, $.ajax({ ... }).progress(function(evl) { console.log(evl); });aber es passiert nichts. Können Sie mir helfen? :)
Patrick DaVader
Welche Version von jQuery verwenden Sie?
Flo Schild
@FloSchild, bitte bearbeiten Sie meinen Code nicht nur aus Gründen Ihrer eigenen Formatierungseinstellungen.
MarkMYoung
3

jQuery verfügt über eine AjaxSetup()Funktion, mit der Sie globale Ajax-Handler wie beforeSendund completefür alle Ajax-Aufrufe registrieren und auf das xhrObjekt zugreifen können, um den gewünschten Fortschritt zu erzielen

Kevin Pei
quelle
2
Danke für den Link. Können Sie Ihrer Antwort ein Beispiel hinzufügen?
Michael Scheper
$ .ajaxSetup ({xhr: function () {console.log ('setup XHR ...');}});
Flo Schild
7
Und ein Beispiel, das die Frage beantwortet? Ich fürchte, ich kann eine Antwort nicht positiv bewerten, die mich viel herumfummeln und lesen lässt, besonders wenn die verlinkte Seite nichts über den Fortschritt aussagt. Ehrlich gesagt, ich bin skeptisch, zumal die Warnung auf der Seite, die sagt Hinweis‘: Rückruf Globale Funktionen sollten mit ihrem jeweiligen globalen Ajax Event - Handler Methoden- gesetzt werden .ajaxStart(), .ajaxStop(), .ajaxComplete(), .ajaxError(), .ajaxSuccess(), .ajaxSend()-rather als in den Optionen Objekt für $.ajaxSetup(). ' < api.jquery.com/jQuery.ajaxSetup/#entry-longdesc >
Michael Scheper
1
Aus den Dokumenten : Legen Sie Standardwerte für zukünftige Ajax-Anforderungen fest. Die Verwendung wird nicht empfohlen.
Oyvind
-1

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

Ich suchte nach einer ähnlichen Lösung und fand diese voll ausnutzen.

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

}}

und Ihre Serverausgaben

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
quelle
Dies zeigt den Fortschritt eines laufenden PHP-Skripts, nicht eines AJAX-Aufrufs.
Sinus Mackowaty
-3

Befolgen Sie die Schritte, um den Fortschritt der Ajax-Anforderung anzuzeigen:

  1. Erstellen Sie einen Spinner mit HTML und CSS oder verwenden Sie Bootstrap Spinner.
  2. Zeigen Sie den Spinner an, wenn der Endbenutzer die AJAX-Daten für die Endlosschleife oder für die Schwellenbegrenzungszeit anfordert.
  3. Entfernen Sie nach einem SUCCESS / ERROR-Ergebnis der AJAX-Anfrage den aktuell angezeigten Spinner und zeigen Sie Ihre Ergebnisse an.

Zur Vereinfachung empfehle ich die Verwendung von JS-Klassen zum dynamischen Anzeigen und Ausblenden des Spinners für diesen Zweck.

Ich hoffe das hilft!

Rahul Gupta
quelle
2
Das ist überhaupt kein "Fortschritt", sondern nur eine "wartende" Animation.
Sinus Mackowaty