Qual è il modo più pulito per ottenere lo stato di avanzamento della richiesta ajax di JQuery?


105

In chiaro javascript è molto semplice: basta allegare il callback a {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);

ma sto facendo un sito ajax che scarica dati html con JQuery ( $.get()o $.ajax()) e mi chiedevo quale sia il modo migliore per ottenere lo stato di avanzamento di una richiesta in modo da visualizzarlo con una piccola barra di avanzamento ma curiosamente non lo sono trovare qualcosa di utile nella documentazione di JQuery ...


4
Questo sembra promettente dave-bond.com/blog/2010/01/JQuery-ajax-progress-HMTL5 per html5
PSL

1
Ooh grazie ragazzi! quindi necessità di sovrascrivere XHR .. la cosa strana è che ho ispezionato con Chrome strumenti di sviluppo il cosiddetto jqXHRoggetto (l'involucro di oggetto XHR restituito da $.ajax()) e ha trovato un progressattributo in esso (insieme a abort, complete, success, etc.), ma nei documenti JQuery manca questo: api.jquery.com/jQuery.ajax/#jqXHR
guari

3
github.com/englercj/jquery-ajax-progress Lo uso ed è più o meno uguale ad altre risposte ma preferisco avere cose più generiche
KeizerBridge,

Risposte:


139

Qualcosa di simile per $.ajax(solo HTML5 però):

$.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
Sembra promettente, ma come può funzionare? L'intera pipeline è composta da tre passaggi: invio di una richiesta, elaborazione della richiesta nel back-end per generare alcuni dati e restituzione. Come può il lato client sapere cosa viene fatto nel backend e quanto tempo ci vorrà per calcolare i progressi?
SexyBeast

1
L'intestazione della risposta HTTP ci dice quanti byte aspettarci, questo progresso sta semplicemente contando quanti byte sono stati ricevuti finora. Rimarrà a zero fino a quando la risposta HTTP non verrà effettivamente inviata
J. Allen

2
Funziona solo su POST, non ha anche GET e gli altri?
Raz

43

jQuery ha già implementato le promesse, quindi è meglio usare questa tecnologia e non spostare la logica degli eventi sul optionsparametro. Ho creato un plugin jQuery che aggiunge promesse di progresso e ora è facile da usare proprio come altre promesse:

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

Dai un'occhiata su GitHub


Mi è piaciuto il modo in cui usi la fabbrica IFI. Non conoscevo quella tecnica!
CodeArtist

Questa è attualmente la migliore soluzione suggerita qui.
atomless

2
Soluzione funzionante ed elegante, ma potresti essere consapevole che può interrompere il codice esistente perché interrompe tutte le chiamate ai deprecati .success e .error. Inoltre rimuove tutti gli attributi non standard impostati su un oggetto jqXHR. Non fornisce anche il contesto in "this" per il callback uploadProgress (forse lo stesso per progresso ma non testato) come è fatto per tutte le promesse standard per jqXHR. Quindi dovrai passare il contesto in una chiusura.
franco

4
Ricevo un errore: TypeError: $.ajax(...).progress(...).progressUpload is not a function.... Qual è il problema?
Universal Grasp

@UniversalGrasp ciao, per favore, apri un problema su GitHub e fornisci informazioni su ciò che hai fatto. Questa libreria non è stata aggiornata per anni :) potrebbe essere cambiato qualcosa in jQuery stesso
likerRr

5

Ho provato tre diversi modi per intercettare la costruzione dell'oggetto Ajax:

  1. Il mio primo tentativo è stato utilizzato xhrFields, ma questo consente solo un ascoltatore, si collega solo all'avanzamento del download (non del caricamento) e richiede ciò che sembra non necessario copia e incolla.
  2. Il mio secondo tentativo ha associato una progressfunzione alla promessa restituita, ma ho dovuto mantenere il mio array di gestori. Non sono riuscito a trovare un buon oggetto per collegare i gestori perché un posto avrei accesso a XHR e un altro avrei avuto accesso a jQuery XHR, ma non ho mai avuto accesso all'oggetto differito (solo la sua promessa).
  3. Il mio terzo tentativo mi ha dato accesso diretto all'XHR per collegare i gestori, ma ancora una volta ho richiesto molto codice di copia e incolla.
  4. Ho concluso il mio terzo tentativo e ho sostituito jQuery ajaxcon il mio. L'unico potenziale difetto è che non puoi più utilizzare le tue xhr()impostazioni. Puoi permetterlo controllando se options.xhrè una funzione.

In realtà chiamo la mia promise.progressfunzione in xhrProgressmodo da poterla trovare facilmente in seguito. Potresti dargli un altro nome per separare i tuoi listener di upload e download. Spero che questo aiuti qualcuno anche se il poster originale ha già ottenuto ciò di cui aveva bisogno.

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

Quindi ho appena provato a implementare la tua soluzione, ma questo codice è un po 'troppo professionale per me da capire: come lo uso? Ho copiato l'intero codice prima del mio document.ready e ho provato a farlo $.ajax({ ... }).progress(function(evl) { console.log(evl); });ma non sta succedendo nulla. Mi potete aiutare? :)
Patrick DaVader

Quale versione di jQuery stai usando?
Flo Schild

@FloSchild, per favore non modificare il mio codice solo per il bene delle tue preferenze di formattazione.
MarkMYoung

3

jQuery ha una AjaxSetup()funzione che ti consente di registrare gestori ajax globali come beforeSende completeper tutte le chiamate ajax e ti consente di accedere xhrall'oggetto per fare i progressi che stai cercando


2
Grazie per il collegamento. Puoi includere un esempio nella tua risposta?
Michael Scheper

$ .ajaxSetup ({xhr: function () {console.log ('setup XHR ...');}});
Flo Schild

7
E un esempio che risponde alla domanda? Temo di non poter votare a favore di una risposta che mi lascia a giocherellare e leggere molto, specialmente quando la pagina collegata non dice nulla sul progresso. Onestamente, io sono scettico su questo, soprattutto in considerazione l'avvertimento in quella pagina, che dice 'Nota: le funzioni di callback globali devono essere impostati con i rispettivi globale metodi- Ajax gestore di eventi .ajaxStart(), .ajaxStop(), .ajaxComplete(), .ajaxError(), .ajaxSuccess(), .ajaxSend()-rather che all'interno delle opzioni oggetto per $.ajaxSetup().' < api.jquery.com/jQuery.ajaxSetup/#entry-longdesc >
Michael Scheper

1
Dai documenti : imposta i valori predefiniti per le future richieste Ajax. Il suo utilizzo è sconsigliato.
Oyvind

-1

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

Stavo cercando una soluzione simile e ho trovato questo completo.

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

}

e gli output del tuo server

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

Questo mostra lo stato di avanzamento di uno script PHP in esecuzione, non di una chiamata AJAX.
Sinus Mackowaty

-3

Segui i passaggi per visualizzare lo stato di avanzamento della richiesta Ajax:

  1. Crea uno Spinner usando Html e CSS o usa Bootstrap Spinner.
  2. Visualizza lo Spinner quando l'utente finale richiede i dati AJAX per il ciclo infinito o per il tempo limite di soglia.
  3. Quindi, dopo un risultato SUCCESS / ERROR della richiesta AJAX, rimuovere lo Spinner attualmente visualizzato e mostrare i risultati.

Per semplificare, ti consiglio di utilizzare le classi JS per visualizzare e nascondere dinamicamente lo spinner a questo scopo.

Spero che aiuti!


2
Questo non significa affatto "ottenere il progresso", ma solo mostrare un'animazione "in attesa".
Sinus Mackowaty
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.