Qual é a maneira mais limpa de obter o progresso da solicitação JQuery ajax?


105

Em javascript puro é muito simples: basta anexar o retorno de chamada para {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);

mas estou fazendo um site ajax que baixa dados html com JQuery ( $.get()ou $.ajax()) e queria saber qual é a melhor maneira de obter o andamento de uma solicitação para exibi-la com uma pequena barra de progresso, mas curiosamente, não estou encontrando algo útil na documentação JQuery ...


4
PSL

1
Ooh obrigado pessoal! por isso necessidade de xhr override .. a coisa estranha é que eu inspecionados com ferramentas Chrome Dev o chamado jqXHRobjeto (o wrapper de objeto xhr retornado por $.ajax()) e encontrou um progressatributo nele (junto com abort, complete, success, etc.), mas nos documentos JQuery está faltando: api.jquery.com/jQuery.ajax/#jqXHR
guari

3
github.com/englercj/jquery-ajax-progress Eu uso isso e é exatamente o mesmo que outras respostas, mas prefiro ter coisas mais genéricas
KeizerBridge

Respostas:


139

Algo assim para $.ajax(embora apenas 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
Parece promissor, mas como isso pode funcionar? Todo o pipeline consiste em três etapas - enviar uma solicitação, processar a solicitação no back-end para gerar alguns dados e retorná-la de volta. Como o lado do cliente pode saber o que está sendo feito no back-end e quanto tempo levará para calcular o progresso?
SexyBeast

1
O cabeçalho de resposta HTTP nos diz quantos bytes esperar, esse progresso é simplesmente contar quantos bytes foram recebidos até agora. Ele permanecerá em zero até que a resposta HTTP seja realmente enviada
J. Allen

2
Está funcionando apenas no POST, não tem GET e os outros também?
Raz

43

jQuery já implementou promessas, então é melhor usar essa tecnologia e não mover a lógica de eventos para optionsparâmetro. Fiz um plugin jQuery que adiciona promessa de progresso e agora é fácil de usar, assim como outras promessas:

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

Confira no github


Gostei da maneira como você usa a fábrica IFI. Eu não conhecia essa técnica!
CodeArtist

Esta é atualmente a melhor solução sugerida aqui.
atomless

2
Solução funcional e elegante, mas você deve estar ciente de que ela pode quebrar seu código existente porque interrompe todas as chamadas para os obsoletos .success e .error. Ele também remove todos os atributos não padrão definidos em um objeto jqXHR. Ele não fornece também o contexto "this" para o retorno de chamada uploadProgress (talvez o mesmo para o progresso, mas não testado), pois é feito para todas as promessas padrão para jqXHR. Portanto, você precisará passar o contexto em um encerramento.
frank

4
Recebo o erro: TypeError: $.ajax(...).progress(...).progressUpload is not a function.... Qual é o problema?
Universal Grasp

@UniversalGrasp oi, por favor, abra um problema no github e forneça informações sobre o que você fez. Esta biblioteca não foi atualizada por muito tempo :) pode ser que algo mudou no próprio jQuery
likerRr

5

Tentei três maneiras diferentes de interceptar a construção do objeto Ajax:

  1. Minha primeira tentativa usada xhrFields, mas que permite apenas para um ouvinte, apenas anexa para download (não upload) progresso e requer o que parece ser copiar e colar desnecessário.
  2. Minha segunda tentativa anexou uma progressfunção à promessa retornada, mas tive que manter minha própria matriz de manipuladores. Não consegui encontrar um bom objeto para anexar os manipuladores porque um lugar teria acesso ao XHR e outro ao jQuery XHR, mas nunca tive acesso ao objeto adiado (apenas sua promessa).
  3. Minha terceira tentativa me deu acesso direto ao XHR para anexar manipuladores, mas novamente exigiu muito código de copiar e colar.
  4. Concluí minha terceira tentativa e substituí a do jQuery pela ajaxminha. A única deficiência potencial é que você não pode mais usar sua própria xhr()configuração. Você pode permitir isso verificando se options.xhré uma função.

Na verdade, chamo minha promise.progressfunção xhrProgresspara que possa encontrá-la facilmente mais tarde. Você pode querer dar outro nome para separar seus ouvintes de upload e download. Espero que isso ajude alguém, mesmo que o autor da postagem original já tenha o que precisava.

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

Acabei de tentar implementar sua solução, mas este código é um pouco profissional demais para eu entender - como faço para usar isso? Copiei colei todo o seu código antes do meu document.ready e tentei fazer, $.ajax({ ... }).progress(function(evl) { console.log(evl); });mas nada está acontecendo. Pode me ajudar? :)
Patrick DaVader

Qual versão do jQuery você está usando?
Flo Schild

@FloSchild, por favor, não edite meu código apenas por causa de suas próprias preferências de formatação.
MarkMYoung

3

jQuery tem uma AjaxSetup()função que permite registrar manipuladores globais de ajax, como beforeSende completepara todas as chamadas de ajax, bem como permite que você acesse o xhrobjeto para fazer o progresso que você está procurando


2
Obrigado pelo link. Você pode incluir um exemplo em sua resposta?
Michael Scheper

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

7
E um exemplo que responde à pergunta? Receio não poder votar a favor de uma resposta que me deixe confundindo e lendo, especialmente quando a página do link não diz nada sobre o progresso. Honestamente, eu sou cético sobre isso, especialmente dado o aviso na página, que diz: 'Nota: As funções de retorno de chamada globais deve ser definido com o respectivo métodos- mundial Ajax manipulador de eventos .ajaxStart(), .ajaxStop(), .ajaxComplete(), .ajaxError(), .ajaxSuccess(), .ajaxSend()-mais que dentro das opções objeto para $.ajaxSetup(). ' < api.jquery.com/jQuery.ajaxSetup/#entry-longdesc >
Michael Scheper

1
Dos documentos : Defina valores padrão para solicitações futuras do Ajax. Seu uso não é recomendado.
Oyvind

-1

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

Eu estava procurando por uma solução semelhante e encontrei este uso 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 as saídas do seu servidor

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

Mostra o progresso de um script PHP em execução, não de uma chamada AJAX.
Sinus Mackowaty de

-3

Siga as etapas para exibir o andamento da solicitação Ajax:

  1. Crie um Spinner usando Html e CSS ou use Bootstrap Spinner.
  2. Exibe o Spinner quando o usuário final está solicitando os Dados AJAX para loop infinito ou para limite de tempo.
  3. Portanto, após um resultado de SUCCESS / ERROR de AJAX Request, remova o Spinner que está sendo exibido no momento e mostre seus resultados.

Para facilitar, eu recomendo que você use classes JS para exibir e ocultar dinamicamente o botão giratório para essa finalidade.

Eu espero que isso ajude!


2
Isso não é "obter o progresso" de forma alguma, apenas mostrar uma animação de "espera".
Sinus Mackowaty de
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.