Apa cara terbersih untuk mendapatkan kemajuan permintaan ajax JQuery?

105

Secara sederhana, javascript sangat sederhana: hanya perlu melampirkan callback ke {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);

tetapi saya melakukan situs ajax yang mengunduh data html dengan JQuery ( $.get()atau $.ajax()) dan saya bertanya-tanya mana cara terbaik untuk mendapatkan kemajuan permintaan untuk menampilkannya dengan sedikit bilah kemajuan tetapi anehnya, saya tidak menemukan sesuatu yang berguna dalam dokumentasi JQuery ...

guari
sumber
4
Yang ini terlihat menjanjikan dave-bond.com/blog/2010/01/JQuery-ajax-progress-HMTL5 untuk html5
PSL
1
Ooh terima kasih teman-teman! sehingga perlu override xhr .. hal yang aneh adalah bahwa saya telah diperiksa dengan Chrome Dev Alat yang disebut jqXHRobjek (pembungkus objek xhr dikembalikan oleh $.ajax()) dan menemukan progressatribut di dalamnya (bersama dengan abort, complete, success, dll), tapi di dokumen JQuery ini hilang: api.jquery.com/jQuery.ajax/#jqXHR
guari
3
github.com/englercj/jquery-ajax-progress Saya menggunakan ini dan ini hampir sama dengan jawaban lain tetapi saya lebih suka memiliki hal
KeizerBridge

Jawaban:

139

Sesuatu seperti ini untuk $.ajax(meskipun hanya 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
sumber
1
Kelihatannya menjanjikan, tapi bagaimana mungkin ini berhasil? Seluruh pipeline terdiri dari tiga langkah - mengirim permintaan, memproses permintaan di backend untuk menghasilkan beberapa data, dan mengembalikannya. Bagaimana mungkin pihak klien mengetahui apa yang sedang dilakukan di backend dan berapa lama waktu yang dibutuhkan untuk menghitung kemajuan?
SexyBeast
1
Header respons HTTP memberi tahu kita berapa banyak byte yang diharapkan, kemajuan ini hanya menghitung berapa banyak byte yang telah diterima sejauh ini. Ini akan tetap nol sampai respons HTTP benar-benar dikirim
J. Allen
2
Apakah hanya bekerja di POST, tidak juga mendapat GET dan lainnya?
Raz
43

jQuery telah mengimplementasikan promise, jadi lebih baik menggunakan teknologi ini dan tidak memindahkan logika peristiwa ke optionsparameter. Saya membuat plugin jQuery yang menambahkan janji kemajuan dan sekarang mudah digunakan seperti janji lainnya:

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

Lihat di github

likerRr
sumber
Saya menyukai cara Anda menggunakan pabrik IFI. Saya tidak tahu teknik itu!
CodeArtist
Saat ini solusi terbaik yang disarankan di sini.
atom
2
Solusi yang berfungsi dan elegan tetapi Anda mungkin menyadari bahwa itu dapat merusak kode yang ada karena itu memutus semua panggilan ke .success dan .error yang tidak digunakan lagi. Ini juga menghapus semua atribut non standar yang Anda setel pada objek jqXHR. Ini juga tidak menyediakan konteks ke dalam "ini" untuk callback uploadProgress (mungkin sama untuk kemajuan tetapi tidak diuji) seperti yang dilakukan untuk semua janji standar untuk jqXHR. Jadi, Anda harus melewatkan konteks dalam sebuah closure.
terang
4
Saya mendapatkan kesalahan: TypeError: $.ajax(...).progress(...).progressUpload is not a function.... Apa masalahnya?
Universal Grasp
@UniversalGrasp hai, tolong, buka masalah di github dan berikan informasi tentang apa yang telah Anda lakukan. Perpustakaan ini tidak diperbarui selama berabad-abad :) mungkin ada sesuatu yang berubah di jQuery itu sendiri
likerRr
5

Saya mencoba sekitar tiga cara berbeda untuk mencegat konstruksi objek Ajax:

  1. Upaya pertama saya digunakan xhrFields, tetapi itu hanya memungkinkan untuk satu pendengar, hanya melampirkan untuk mengunduh (bukan mengunggah) kemajuan, dan memerlukan apa yang tampaknya seperti salin dan tempel yang tidak perlu.
  2. Upaya kedua saya melampirkan progressfungsi ke janji yang dikembalikan, tetapi saya harus mempertahankan susunan penangan saya sendiri. Saya tidak dapat menemukan objek yang baik untuk dilampirkan penangan karena satu tempat saya akan mengakses XHR dan tempat lain saya memiliki akses ke jQuery XHR, tetapi saya tidak pernah memiliki akses ke objek yang ditangguhkan (hanya janjinya).
  3. Upaya ketiga saya memberi saya akses langsung ke XHR untuk memasang penangan, tetapi sekali lagi memerlukan banyak kode salin dan tempel.
  4. Saya menyelesaikan upaya ketiga saya dan mengganti jQuery ajaxdengan milik saya. Satu-satunya kekurangan potensial adalah Anda tidak dapat lagi menggunakan xhr()pengaturan Anda sendiri . Anda dapat mengizinkannya dengan memeriksa apakah options.xhrsuatu fungsi.

Saya sebenarnya memanggil promise.progressfungsi saya xhrProgresssehingga saya dapat menemukannya dengan mudah nanti. Anda mungkin ingin menamainya dengan nama lain untuk memisahkan upload dan download listener Anda. Saya harap ini membantu seseorang meskipun poster aslinya sudah mendapatkan apa yang dia butuhkan.

(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
sumber
Jadi saya baru saja mencoba menerapkan solusi Anda tetapi kode ini agak terlalu profesional untuk saya pahami - bagaimana cara menggunakan ini? Saya menyalin menempelkan seluruh kode Anda sebelum dokumen saya. Sudah dan mencoba melakukan $.ajax({ ... }).progress(function(evl) { console.log(evl); });tetapi tidak ada yang terjadi. Bisakah kamu membantuku? :)
Patrick DaVader
Versi jQuery mana yang Anda gunakan?
Flo Schild
@FloSchild, mohon jangan edit kode saya hanya demi preferensi pemformatan Anda sendiri.
MarkMYoung
3

jQuery memiliki AjaxSetup()fungsi yang memungkinkan Anda mendaftarkan penangan ajax global seperti beforeSenddan completeuntuk semua panggilan ajax serta memungkinkan Anda mengakses xhrobjek untuk melakukan kemajuan yang Anda cari.

Kevin Pei
sumber
2
Terima kasih untuk tautannya. Bisakah Anda memasukkan contoh dalam jawaban Anda?
Michael Scheper
$ .ajaxSetup ({xhr: function () {console.log ('setup XHR ...');}});
Flo Schild
7
Dan contoh yang menjawab pertanyaan itu? Saya khawatir saya tidak dapat memberikan suara positif pada jawaban yang membuat saya banyak mengutak-atik dan membaca, terutama ketika halaman tertaut tidak mengatakan apa-apa tentang kemajuan. Jujur, aku skeptis tentang ini, terutama mengingat peringatan pada halaman tersebut, yang mengatakan 'Catatan: fungsi callback global harus ditetapkan dengan masing-global yang methods- Ajax event mereka .ajaxStart(), .ajaxStop(), .ajaxComplete(), .ajaxError(), .ajaxSuccess(), .ajaxSend()-rather dari dalam pilihan objek untuk $.ajaxSetup(). ' < api.jquery.com/jQuery.ajaxSetup/#entry-longdesc >
Michael Scheper
1
Dari dokumen : Setel nilai default untuk permintaan Ajax di masa mendatang. Penggunaannya tidak disarankan.
Oyvind
-1

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

Saya sedang mencari solusi serupa dan menemukan ini digunakan penuh.

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

}

dan keluaran server Anda

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
sumber
Ini menunjukkan kemajuan dari skrip PHP yang sedang berjalan, bukan dari panggilan AJAX.
Sinus Mackowaty
-3

Ikuti langkah-langkah untuk menampilkan Progress of Ajax Request:

  1. Buat Spinner menggunakan Html & CSS atau gunakan Bootstrap Spinner.
  2. Menampilkan Spinner ketika pengguna akhir meminta Data AJAX untuk loop tak terbatas atau waktu batas ambang.
  3. Jadi, setelah hasil SUCCESS / ERROR dari Permintaan AJAX, hapus Spinner yang saat ini ditampilkan dan tunjukkan hasil Anda.

Untuk membuatnya mudah, saya sarankan Anda menggunakan Kelas JS untuk secara dinamis Menampilkan & Menyembunyikan spinner untuk tujuan ini.

Saya harap ini membantu!

Rahul Gupta
sumber
2
Itu sama sekali bukan "mendapatkan kemajuan", hanya menampilkan animasi "menunggu".
Sinus Mackowaty