วิธีใดเป็นวิธีที่สะอาดที่สุดในการรับความคืบหน้าของคำขอ jQuery ajax


105

ในจาวาสคริปต์ธรรมดานั้นง่ายมากเพียงแค่แนบการโทรกลับไปที่ {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);

แต่ฉันกำลังทำไซต์ ajax ที่ดาวน์โหลดข้อมูล html ด้วย JQuery ( $.get()หรือ$.ajax()) และฉันสงสัยว่าวิธีใดเป็นวิธีที่ดีที่สุดในการรับความคืบหน้าของคำขอเพื่อแสดงด้วยแถบความคืบหน้าเล็กน้อย แต่ฉันไม่อยากรู้ ค้นหาสิ่งที่เป็นประโยชน์ในเอกสาร JQuery ...


4
อันนี้ดูมีแนวโน้มdave-bond.com/blog/2010/01/JQuery-ajax-progress-HMTL5สำหรับ html5
PSL

1
โอขอบคุณทุกคน! จึงไม่จำเป็นต้องแทนที่ XHR .. สิ่งที่แปลกคือที่ฉันได้รับการตรวจสอบด้วยเครื่องมือ Chrome Dev ที่เรียกว่าjqXHRวัตถุ (เสื้อคลุมของวัตถุ XHR ส่งกลับโดย$.ajax()) และพบว่าprogressแอตทริบิวต์ในนั้น (พร้อมด้วยabort, complete, successฯลฯ ) แต่ ในเอกสาร JQuery สิ่งนี้หายไป: api.jquery.com/jQuery.ajax/#jqXHR
guari

3
github.com/englercj/jquery-ajax-progressฉันใช้สิ่งนี้และมันค่อนข้างเหมือนกับคำตอบอื่น ๆ แต่ฉันชอบที่จะมีของทั่วไปมากกว่า
KeizerBridge

คำตอบ:


138

สิ่งนี้สำหรับ$.ajax(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
    }
});

1
ดูมีแนวโน้ม แต่วิธีนี้จะได้ผล? ไปป์ไลน์ทั้งหมดประกอบด้วยสามขั้นตอนคือการส่งคำขอประมวลผลคำขอในแบ็กเอนด์เพื่อสร้างข้อมูลบางส่วนและส่งคืนกลับ ฝั่งไคลเอ็นต์จะรู้ได้อย่างไรว่ากำลังทำอะไรอยู่ในแบ็กเอนด์และจะต้องใช้เวลาเท่าไรในการคำนวณความคืบหน้า
SexyBeast

1
ส่วนหัวการตอบกลับ HTTP จะบอกให้เราทราบจำนวนไบต์ที่คาดหวังความคืบหน้านี้เป็นการนับจำนวนไบต์ที่ได้รับจนถึงขณะนี้ มันจะอยู่ที่ศูนย์จนกว่าการตอบกลับ HTTP จะถูกส่งจริง
J. Allen

2
มันใช้งานได้เฉพาะใน POST เท่านั้นไม่ได้รับ GET และอื่น ๆ ด้วยหรือไม่?
Raz

43

jQuery ได้ดำเนินการตามสัญญาแล้วดังนั้นจึงเป็นการดีกว่าที่จะใช้เทคโนโลยีนี้และไม่ย้ายตรรกะเหตุการณ์ไปที่optionsพารามิเตอร์ ฉันสร้างปลั๊กอิน jQuery ที่เพิ่มสัญญาความคืบหน้าและตอนนี้มันใช้งานง่ายเหมือนกับสัญญาอื่น ๆ :

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

ลองดูที่github


ฉันชอบวิธีที่คุณใช้โรงงาน IFI ฉันไม่รู้เทคนิคนั้น!
CodeArtist

นี่เป็นทางออกที่ดีที่สุดที่แนะนำที่นี่
อะตอม

2
โซลูชันที่ใช้งานได้และสวยงาม แต่คุณอาจทราบว่ามันสามารถทำลายโค้ดที่มีอยู่ของคุณได้เนื่องจากมันทำลายการเรียกทั้งหมดไปยัง .success และ. error นอกจากนี้ยังตัดแอตทริบิวต์ที่ไม่ได้มาตรฐานทั้งหมดที่คุณตั้งไว้บนวัตถุ jqXHR นอกจากนี้ยังไม่ได้ระบุบริบทใน "this" สำหรับการเรียกกลับ uploadProgress (อาจจะเหมือนกันสำหรับความคืบหน้า แต่ไม่ได้ทดสอบ) เช่นเดียวกับที่ทำกับสัญญามาตรฐานทั้งหมดสำหรับ jqXHR ดังนั้นคุณจะต้องผ่านบริบทในการปิด
ตรงไปตรง

4
ฉันได้รับข้อผิดพลาด: TypeError: $.ajax(...).progress(...).progressUpload is not a function.... ปัญหาคืออะไร?
Universal Grasp

@UniversalGrasp สวัสดีโปรดเปิดปัญหาที่ github และให้ข้อมูลเกี่ยวกับสิ่งที่คุณทำ ห้องสมุดนี้ไม่ได้อัปเดตมานานแล้ว :) อาจมีบางอย่างเปลี่ยนแปลงไปใน jQuery เอง
likerRr

5

ฉันลองใช้วิธีสกัดกั้นการสร้างวัตถุ Ajax สามวิธี:

  1. ความพยายามครั้งแรกของฉันใช้xhrFieldsแต่อนุญาตให้มีผู้ฟังเพียงคนเดียวเท่านั้นที่แนบกับความคืบหน้าในการดาวน์โหลด (ไม่ใช่การอัปโหลด) และต้องการสิ่งที่ดูเหมือนว่าจะคัดลอกและวางโดยไม่จำเป็น
  2. ความพยายามครั้งที่สองของฉันแนบprogressฟังก์ชันเข้ากับสัญญาที่ส่งคืน แต่ฉันต้องรักษาอาร์เรย์ของตัวจัดการของฉันเอง ฉันไม่พบวัตถุที่ดีในการแนบตัวจัดการเพราะที่แห่งหนึ่งฉันเข้าถึง XHR และอีกแห่งหนึ่งฉันสามารถเข้าถึง jQuery XHR ได้ แต่ฉันไม่เคยเข้าถึงวัตถุที่รอการตัดบัญชี (มีเพียงสัญญาเท่านั้น)
  3. ความพยายามครั้งที่สามของฉันทำให้ฉันสามารถเข้าถึง XHR ได้โดยตรงเพื่อเชื่อมต่อตัวจัดการ แต่จำเป็นต้องใช้รหัสคัดลอกและวางอีกครั้ง
  4. ฉันปิดความพยายามครั้งที่สามและแทนที่ jQuery ajaxด้วยของฉันเอง ข้อบกพร่องเดียวที่อาจเกิดขึ้นคือคุณไม่สามารถใช้xhr()การตั้งค่าของคุณเองได้อีกต่อไป คุณสามารถอนุญาตได้โดยตรวจสอบว่าoptions.xhrเป็นฟังก์ชันหรือไม่

ฉันเรียกpromise.progressฟังก์ชันของฉันจริงๆxhrProgressเพื่อที่ฉันจะได้ค้นหาได้ง่ายในภายหลัง คุณอาจต้องการตั้งชื่ออย่างอื่นเพื่อแยกผู้ฟังที่อัปโหลดและดาวน์โหลด ฉันหวังว่านี่จะช่วยใครสักคนแม้ว่าโปสเตอร์ต้นฉบับจะมีสิ่งที่เขาต้องการอยู่แล้วก็ตาม

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

ดังนั้นฉันจึงลองใช้โซลูชันของคุณ แต่โค้ดนี้ค่อนข้างโปรเกินไปสำหรับฉันที่จะเข้าใจฉันจะใช้สิ่งนี้ได้อย่างไร ฉันคัดลอกวางรหัสทั้งหมดของคุณก่อนเอกสารของฉันแล้วและพยายามทำ$.ajax({ ... }).progress(function(evl) { console.log(evl); });แต่ไม่มีอะไรเกิดขึ้น คุณสามารถช่วยฉันได้ไหม? :)
Patrick DaVader

คุณใช้ jQuery เวอร์ชันใดอยู่
Flo Schild

@FloSchild โปรดอย่าแก้ไขโค้ดของฉันเพียงเพื่อประโยชน์ในการกำหนดรูปแบบของคุณเอง
MarkMYoung

3

jQuery มีAjaxSetup()ฟังก์ชันที่ให้คุณลงทะเบียนตัวจัดการ ajax ระดับโลกเช่นbeforeSendและcompleteสำหรับการโทรของ ajax ทั้งหมดรวมทั้งอนุญาตให้คุณเข้าถึงxhrวัตถุเพื่อดำเนินการตามที่คุณกำลังมองหา


2
ขอบคุณสำหรับลิงค์ คุณสามารถใส่ตัวอย่างในคำตอบของคุณได้หรือไม่?
Michael Scheper

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

7
และตัวอย่างที่ตอบคำถาม? ฉันกลัวว่าจะไม่สามารถโหวตคำตอบที่ทำให้ฉันต้องเล่นซอและอ่านหนังสือมากมายโดยเฉพาะอย่างยิ่งเมื่อหน้าที่เชื่อมโยงบอกว่าไม่มีความคืบหน้า สุจริตฉันสงสัยเกี่ยวกับเรื่องนี้โดยเฉพาะอย่างยิ่งคำเตือนบนหน้านั้นว่า 'หมายเหตุ: ฟังก์ชั่นการโทรกลับทั่วโลกควรจะตั้งกับอาแจ็กซ์ทั่วโลก methods- จัดการเหตุการณ์ของตน.ajaxStart(), .ajaxStop(), .ajaxComplete(), .ajaxError(), .ajaxSuccess(), .ajaxSend()-rather กว่าภายในตัวเลือกสำหรับวัตถุ$.ajaxSetup(). ' < api.jquery.com/jQuery.ajaxSetup/#entry-longdesc >
Michael Scheper

1
จากเอกสาร : ตั้งค่าเริ่มต้นสำหรับคำขอ Ajax ในอนาคต ไม่แนะนำให้ใช้
Oyvind

-1

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

ฉันกำลังค้นหาวิธีแก้ปัญหาที่คล้ายกันและพบว่าอันนี้ใช้เต็ม

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

}

และเซิร์ฟเวอร์ของคุณส่งออก

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

สิ่งนี้แสดงความคืบหน้าของสคริปต์ PHP ที่กำลังทำงานไม่ใช่ของการเรียก AJAX
Sinus Mackowaty

-3

ทำตามขั้นตอนเพื่อแสดงความคืบหน้าของคำขอ Ajax:

  1. สร้าง Spinner โดยใช้ Html & CSS หรือใช้ Bootstrap Spinner
  2. แสดง Spinner เมื่อผู้ใช้ปลายทางร้องขอข้อมูล AJAX สำหรับการวนซ้ำแบบไม่มีที่สิ้นสุดหรือสำหรับเวลา จำกัด ขีด จำกัด
  3. ดังนั้นหลังจากผลลัพธ์ SUCCESS / ERROR ของ AJAX Request ให้ลบ Spinner ที่กำลังแสดงอยู่และแสดงผลลัพธ์ของคุณ

เพื่อให้ง่ายฉันขอแนะนำให้คุณใช้ JS Classes เพื่อแสดงและซ่อนสปินเนอร์แบบไดนามิกเพื่อจุดประสงค์นี้

ฉันหวังว่านี่จะช่วยได้!


2
นั่นไม่ใช่การ "รับความคืบหน้า" แต่เป็นการแสดงภาพเคลื่อนไหว "รอ" เท่านั้น
Sinus Mackowaty
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.