Quel est le moyen le plus propre d'obtenir la progression de la requête JQuery ajax?

105

En javascript simple, c'est très simple: il suffit de joindre le rappel à {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);

mais je fais un site ajax qui télécharge des données html avec JQuery ( $.get()ou $.ajax()) et je me demandais quel est le meilleur moyen d'obtenir la progression d'une requête afin de l'afficher avec une petite barre de progression mais curieusement, je ne suis pas trouver quelque chose d'utile dans la documentation JQuery ...

guari
la source
4
Celui-ci semble prometteur dave-bond.com/blog/2010/01/JQuery-ajax-progress-HMTL5 pour html5
PSL
1
Ooh merci les gars! si besoin de modifier XHR .. la chose étrange est que je l' ai inspecté avec les outils Chrome Dev que l'on appelle jqXHRobjet (l'emballage de l' objet XHR retourné par $.ajax()) et a trouvé un progressattribut dans ce (avec abort, complete, success, etc.), mais dans la documentation JQuery, il manque: api.jquery.com/jQuery.ajax/#jqXHR
guari
3
github.com/englercj/jquery-ajax-progress J'utilise ceci et c'est tout à fait la même chose que les autres réponses mais je préfère avoir des choses plus génériques
KeizerBridge

Réponses:

139

Quelque chose comme ça pour $.ajax(HTML5 uniquement):

$.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
la source
1
Cela semble prometteur, mais comment cela peut-il fonctionner? L'ensemble du pipeline se compose de trois étapes: l'envoi d'une requête, le traitement de la requête dans le backend pour générer des données et la renvoyer. Comment le côté client peut-il savoir ce qui est fait dans le backend et combien de temps il lui faudra pour calculer la progression?
SexyBeast
1
L'en-tête de la réponse HTTP nous indique le nombre d'octets à attendre, cette progression compte simplement le nombre d'octets reçus jusqu'à présent. Il restera à zéro jusqu'à ce que la réponse HTTP soit réellement envoyée
J. Allen
2
Cela fonctionne-t-il uniquement sur POST, pas aussi sur GET et les autres?
Raz
43

jQuery a déjà implémenté des promesses, il est donc préférable d'utiliser cette technologie et de ne pas déplacer la logique des événements vers le optionsparamètre. J'ai fait un plugin jQuery qui ajoute une promesse de progrès et maintenant il est facile à utiliser comme les autres promesses:

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

Découvrez-le sur github

likerRr
la source
J'ai aimé la façon dont vous utilisez l'usine IFI. Je ne connaissais pas cette technique!
CodeArtist
C'est actuellement la meilleure solution proposée ici.
atomless
2
Solution fonctionnelle et élégante, mais vous savez peut-être qu'elle peut casser votre code existant car elle interrompt tous les appels aux fichiers .success et .error obsolètes. Il supprime également tous les attributs non standard que vous définissez sur un objet jqXHR. Il ne fournit pas également le contexte dans "this" pour le rappel uploadProgress (peut-être le même pour progress mais pas testé) comme il est fait pour toutes les promesses standard pour jqXHR. Vous devrez donc passer le contexte dans une clôture.
franc
4
J'obtiens une erreur: TypeError: $.ajax(...).progress(...).progressUpload is not a function... Quel est le problème?
Universal Grasp
@UniversalGrasp salut, s'il vous plaît, ouvrez un problème sur github et fournissez des informations sur ce que vous avez fait. Cette bibliothèque n'a pas été mise à jour depuis des lustres :) peut-être quelque chose a changé dans jQuery lui
likerRr
5

J'ai essayé trois manières différentes d'intercepter la construction de l'objet Ajax:

  1. Ma première tentative a utilisé xhrFields , mais cela ne permet qu'un seul auditeur, ne s'attache qu'à la progression du téléchargement (pas du téléchargement) et nécessite ce qui semble être un copier-coller inutile.
  2. Ma deuxième tentative a joint un progress fonction à la promesse retournée, mais j'ai dû maintenir mon propre tableau de gestionnaires. Je n'ai pas pu trouver un bon objet pour attacher les gestionnaires car un endroit où j'accéderais au XHR et un autre j'aurais accès au jQuery XHR, mais je n'ai jamais eu accès à l'objet différé (seulement sa promesse).
  3. Ma troisième tentative m'a donné un accès direct au XHR pour attacher des gestionnaires, mais encore une fois, il a fallu beaucoup de copier-coller du code.
  4. J'ai terminé ma troisième tentative et remplacé jQuery ajaxpar le mien. Le seul inconvénient potentiel est que vous ne pouvez plus utiliser votre propre xhr()réglage. Vous pouvez permettre cela en vérifiant s'il options.xhrs'agit d'une fonction.

J'appelle ma promise.progressfonction xhrProgressafin de pouvoir la retrouver facilement plus tard. Vous voudrez peut-être lui donner un autre nom pour séparer vos écouteurs de téléchargement et de téléchargement. J'espère que cela aide quelqu'un même si l'affiche originale a déjà ce dont il avait besoin.

(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
la source
Alors j'ai juste essayé d'implémenter votre solution mais ce code est un peu trop pro pour que je le comprenne - comment l'utiliser? J'ai copié collé tout votre code avant mon document.ready et j'ai essayé de le faire $.ajax({ ... }).progress(function(evl) { console.log(evl); });mais rien ne se passe. Pouvez-vous m'aider? :)
Patrick DaVader
Quelle version de jQuery utilisez-vous?
Flo Schild
@FloSchild, veuillez ne pas modifier mon code uniquement pour vos propres préférences de formatage.
MarkMYoung
3

jQuery a une AjaxSetup()fonction qui vous permet d'enregistrer des gestionnaires ajax globaux tels que beforeSendet completepour tous les appels ajax ainsi que vous permet d'accéder à l' xhrobjet pour faire la progression que vous recherchez

Kevin Pei
la source
2
Merci pour le lien. Pouvez-vous inclure un exemple dans votre réponse?
Michael Scheper
$ .ajaxSetup ({xhr: function () {console.log ('setup XHR ...');}});
Flo Schild
7
Et un exemple qui répond à la question? J'ai peur de ne pas pouvoir voter pour une réponse qui me laisse beaucoup de bidouillage et de lecture, surtout lorsque la page liée ne dit rien sur les progrès. Honnêtement, je suis sceptique à ce sujet, surtout compte tenu de l'avertissement sur cette page qui dit « Remarque: Les fonctions de rappel globales doivent être définies avec leur gestionnaire d'événements global Ajax respectifs méthodes- .ajaxStart(), .ajaxStop(), .ajaxComplete(), .ajaxError(), .ajaxSuccess(), .ajaxSend()-plutôt que dans les options pour objet $.ajaxSetup()». < api.jquery.com/jQuery.ajaxSetup/#entry-longdesc >
Michael Scheper
1
À partir de la documentation : définissez les valeurs par défaut pour les futures requêtes Ajax. Son utilisation n'est pas recommandée.
Oyvind
-1

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

Je cherchais une solution similaire et j'ai trouvé que celle-ci était pleinement utilisée.

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

}

et vos sorties serveur

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
la source
Cela montre la progression d'un script PHP en cours d'exécution, pas d'un appel AJAX.
Sinus Mackowaty
-3

Suivez les étapes pour afficher la progression de la requête Ajax:

  1. Créez un Spinner en utilisant Html et CSS ou utilisez Bootstrap Spinner.
  2. Affichez le Spinner lorsque l'utilisateur final demande les données AJAX pour une boucle infinie ou pour un temps limite de seuil.
  3. Ainsi, après un résultat SUCCÈS / ERREUR de la requête AJAX, supprimez le Spinner qui est actuellement affiché et affichez vos résultats.

Pour vous faciliter la tâche, je vous recommande d'utiliser les classes JS pour afficher et masquer dynamiquement le spinner à cet effet.

J'espère que ça aide!

Rahul Gupta
la source
2
Ce n'est pas du tout "obtenir la progression", mais uniquement une animation "en attente".
Sinus Mackowaty