Interromper solicitações Ajax usando jQuery

1827

Usando o jQuery, como posso cancelar / abortar uma solicitação do Ajax da qual ainda não recebi a resposta?

morno
fonte
2
Talvez eu tenha entendido mal a pergunta, mas você não poderia simplesmente usar $.ajaxStop?
Luke Madhanga
17
@LukeMadhanga; Eu acho que ajaxStopsó detecta quando todas as solicitações AJAX são concluídas, não para uma solicitação. Na minha experiência, você iria utilizá-lo como este: $(document).ajaxStop(function(){alert("All done")}). Usar .abort()é a melhor opção.
TheCarver

Respostas:

1744

A maioria dos métodos jQuery Ajax retorna um objeto XMLHttpRequest (ou equivalente), para que você possa usar abort().

Veja a documentação:

  • abortar ( MSDN ). Cancela a solicitação HTTP atual.
  • abortar () ( MDN ). Se a solicitação já tiver sido enviada, esse método abortará a solicitação.
var xhr = $.ajax({
    type: "POST",
    url: "some.php",
    data: "name=John&location=Boston",
    success: function(msg){
       alert( "Data Saved: " + msg );
    }
});

//kill the request
xhr.abort()

ATUALIZAÇÃO: A partir do jQuery 1.5, o objeto retornado é um wrapper para o objeto XMLHttpRequest nativo chamado jqXHR. Este objeto parece expor todas as propriedades e métodos nativos para que o exemplo acima ainda funcione. Consulte O objeto jqXHR (documentação da API jQuery).

ATUALIZAÇÃO 2: A partir do jQuery 3, o método ajax agora retorna uma promessa com métodos extras (como abortar), portanto o código acima ainda funciona, embora o objeto que está sendo retornado não xhrseja mais. Veja o blog 3.0 aqui .

ATUALIZAÇÃO 3 : xhr.abort()ainda funciona no jQuery 3.x. Não assuma que a atualização 2 esteja correta. Mais informações sobre o repositório jQuery Github .

meouw
fonte
12
É como clicar no botão 'parar' no seu navegador. Cancela a solicitação. Você pode reutilizar o mesmo objeto XHR para outra solicitação.
meouw 15/01/09
65
@brad Infelizmente, abortnão fecha uma conexão estabelecida com o servidor, por successisso ainda será chamado. Isso é muito problemático para implementações do Comet com pesquisas longas.
pepkin88
6
@ErvWalter Você pode enviar outra solicitação que cancela a anterior.
Asad Saeeduddin
8
Em meus testes, a solicitação será abortada no navegador, mas o .fail () é chamado com um jqXHR, status = 0, e jqXHR, statusMessage = "abortar"
BJ Safdie
9
.abort()ainda funciona para mim no jQuery 3.2.1. Esse comentário em um problema relacionado ao github por um membro da equipe principal do jQuery parece sugerir que esta resposta está incorreta e que .abort() não foi removida.
alarive
117

Você não pode recuperar a solicitação, mas pode definir um valor de tempo limite após o qual a resposta será ignorada. Veja esta página para opções de jquery AJAX. Acredito que seu retorno de chamada de erro será chamado se o período de tempo limite for excedido. Já existe um tempo limite padrão em cada solicitação AJAX.

Você também pode usar o abort () método no objeto do pedido, mas, ao mesmo tempo que fará com que o cliente para parar de ouvir para o evento, ele pode provavelmente não vai parar o servidor de processá-lo.

tvanfosson
fonte
74

É uma solicitação assíncrona , ou seja, uma vez enviada, ela está lá fora.

Caso seu servidor esteja iniciando uma operação muito cara devido à solicitação AJAX, o melhor que você pode fazer é abrir o servidor para ouvir solicitações de cancelamento e enviar uma solicitação AJAX separada notificando o servidor para parar o que está fazendo.

Caso contrário, simplesmente ignore a resposta AJAX.

Yuval Adam
fonte
48
Nesse contexto, assíncrono significa simplesmente que a solicitação não interrompe o fluxo do script. Agora, os navegadores podem abortar a solicitação prematuramente antes que ela seja concluída.
ElephantHunter
4
Como podemos enviar uma solicitação de cancelamento ao servidor? Não entendo como o servidor saberá qual solicitação interromperá o processamento? Você poderia dar um exemplo? Obrigado.
Lucky Soni
3
@LuckySoni, a Elephanthunter está discutindo apenas o lado do cliente. O servidor não será afetado por um pedido .abort
Kloar
4
@Kloar, se a solicitação ainda não for recebida pelo servidor (por exemplo, grandes solicitações de várias partes), o cliente poderá fechar o soquete e o servidor será afetado.
white
4
O PHP verifica automaticamente pedidos abortados e termina automaticamente também. Veja php.net/manual/en/function.ignore-user-abort.php
hanshenrik
68

Salve as chamadas feitas em uma matriz e chame xhr.abort () em cada uma.

ENORME CAVEAT: Você pode abortar uma solicitação, mas esse é apenas o lado do cliente. O lado do servidor ainda pode estar processando a solicitação. Se você estiver usando algo como PHP ou ASP com dados da sessão, os dados da sessão serão bloqueados até o final do ajax. Portanto, para permitir que o usuário continue navegando no site, é necessário chamar session_write_close (). Isso salva a sessão e a desbloqueia para que outras páginas que aguardam a continuação continuem. Sem isso, várias páginas podem estar aguardando a remoção do bloqueio.

Tei
fonte
desde que você sempre cancela a anterior, você não pode sempre fazer isso? (e não há necessidade de usar uma matriz)
polaridade
60

As solicitações AJAX podem não ser concluídas na ordem em que foram iniciadas. Em vez de abortar, você pode optar por ignorar todas as respostas AJAX, exceto a mais recente:

  • Crie um contador
  • Incremente o contador ao iniciar a solicitação AJAX
  • Use o valor atual do contador para "carimbar" a solicitação
  • No retorno de chamada bem-sucedido, compare o carimbo com o contador para verificar se foi o pedido mais recente

Esboço aproximado do código:

var xhrCount = 0;
function sendXHR() {
    // sequence number for the current invocation of function
    var seqNumber = ++xhrCount;
    $.post("/echo/json/", { delay: Math.floor(Math.random() * 5) }, function() {
        // this works because of the way closures work
        if (seqNumber === xhrCount) {
            console.log("Process the response");
        } else {
            console.log("Ignore the response");
        }
    });
}
sendXHR();
sendXHR();
sendXHR();
// AJAX requests complete in any order but only the last 
// one will trigger "Process the response" message

Demo no jsFiddle

Salman A
fonte
Devido às advertências de cancelamento de uma solicitação XHR (consulte outros comentários nesta página), esta é provavelmente a melhor e mais simples opção.
Quinn Comendant
41

Nós apenas tivemos que solucionar esse problema e testamos três abordagens diferentes.

  1. cancela a solicitação conforme sugerido por @meouw
  2. executa todas as solicitações, mas processa apenas o resultado do último envio
  3. impede novos pedidos enquanto outro ainda estiver pendente

var Ajax1 = {
  call: function() {
    if (typeof this.xhr !== 'undefined')
      this.xhr.abort();
    this.xhr = $.ajax({
      url: 'your/long/running/request/path',
      type: 'GET',
      success: function(data) {
        //process response
      }
    });
  }
};
var Ajax2 = {
  counter: 0,
  call: function() {
    var self = this,
      seq = ++this.counter;
    $.ajax({
      url: 'your/long/running/request/path',
      type: 'GET',
      success: function(data) {
        if (seq === self.counter) {
          //process response
        }
      }
    });
  }
};
var Ajax3 = {
  active: false,
  call: function() {
    if (this.active === false) {
      this.active = true;
      var self = this;
      $.ajax({
        url: 'your/long/running/request/path',
        type: 'GET',
        success: function(data) {
          //process response
        },
        complete: function() {
          self.active = false;
        }
      });
    }
  }
};
$(function() {
  $('#button').click(function(e) {
    Ajax3.call();
  });
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="button" type="button" value="click" />

No nosso caso, decidimos usar a abordagem nº 3, pois ela produz menos carga para o servidor. Mas não tenho 100% de certeza se o jQuery garantir a chamada do método .complete () -, isso pode produzir uma situação de conflito. Nos nossos testes, não conseguimos reproduzir essa situação.

oiofante
fonte
36

É sempre uma prática recomendada fazer algo assim.

var $request;
if ($request != null){ 
    $request.abort();
    $request = null;
}

$request = $.ajax({
    type : "POST", //TODO: Must be changed to POST
    url : "yourfile.php",
    data : "data"
    }).done(function(msg) {
        alert(msg);
    });

Mas é muito melhor se você verificar uma instrução if para verificar se a solicitação ajax é nula ou não.

Tharindu Kumara
fonte
2
Também verifico se uma solicitação já está em execução, mas uso um booleano, pois é mais leve.
Zesty 29/07
15

Basta ligar para xhr. abort (), seja objeto jquery ajax ou objeto XMLHTTPRequest nativo.

exemplo:

//jQuery ajax
$(document).ready(function(){
    var xhr = $.get('/server');
    setTimeout(function(){xhr.abort();}, 2000);
});

//native XMLHTTPRequest
var xhr = new XMLHttpRequest();
xhr.open('GET','/server',true);
xhr.send();
setTimeout(function(){xhr.abort();}, 2000);
cuixiping
fonte
13

Eu estava fazendo uma solução de pesquisa ao vivo e precisava cancelar solicitações pendentes que podem levar mais tempo que a solicitação mais recente / mais atual.

No meu caso, usei algo como isto:

//On document ready
var ajax_inprocess = false;

$(document).ajaxStart(function() {
ajax_inprocess = true;
});

$(document).ajaxStop(function() {
ajax_inprocess = false;
});

//Snippet from live search function
if (ajax_inprocess == true)
{
    request.abort();
}
//Call for new request 
Billy
fonte
12

Como muitas pessoas no encadeamento observaram, apenas porque a solicitação foi abortada no lado do cliente, o servidor ainda processará a solicitação. Isso cria uma carga desnecessária no servidor, porque ele está fazendo um trabalho que paramos de ouvir no front-end.

O problema que eu estava tentando resolver (para o qual outras pessoas também se deparam) é que, quando o usuário inseria informações em um campo de entrada, eu queria acionar uma solicitação para uma sensação do tipo Google Instant.

Para evitar disparar solicitações desnecessárias e manter o snappiness do front-end, fiz o seguinte:

var xhrQueue = [];
var xhrCount = 0;

$('#search_q').keyup(function(){

    xhrQueue.push(xhrCount);

    setTimeout(function(){

        xhrCount = ++xhrCount;

        if (xhrCount === xhrQueue.length) {
            // Fire Your XHR //
        }

    }, 150);

});

Essencialmente, isso enviará uma solicitação a cada 150ms (uma variável que você pode personalizar para suas próprias necessidades). Se você estiver com problemas para entender o que exatamente está acontecendo aqui, faça logon xhrCounte xhrQueueno console imediatamente antes do bloco if.

brianrhea
fonte
11

Basta usar ajax.abort (), por exemplo, você pode abortar qualquer solicitação de ajax pendente antes de enviar outra como esta

//check for existing ajax request
if(ajax){ 
 ajax.abort();
 }
//then you make another ajax request
$.ajax(
 //your code here
  );
Joecoder001
fonte
11

Você pode cancelar qualquer chamada ajax contínua usando este

<input id="searchbox" name="searchbox" type="text" />

<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>

<script type="text/javascript">
     var request = null;
        $('#searchbox').keyup(function () {
            var id = $(this).val();
            request = $.ajax({
                type: "POST", //TODO: Must be changed to POST
                url: "index.php",
                data: {'id':id},
                success: function () {

                },
                beforeSend: function () {
                    if (request !== null) {
                        request.abort();
                    }
                }
            });
        });

</script>
Soubhagya Kumar Barik
fonte
10

não há uma maneira confiável de fazer isso, e eu nem tentaria, uma vez que a solicitação estivesse em movimento; a única maneira de reagir razoavelmente é ignorar a resposta.

na maioria dos casos, isso pode acontecer em situações como: um usuário clica com muita freqüência em um botão que aciona muitos XHR consecutivos, aqui você tem muitas opções: bloqueia o botão até o retorno do XHR ou nem aciona um novo XHR enquanto outro está executando dicas o usuário se inclinar para trás - ou descartar qualquer resposta XHR pendente, exceto a recente.

comeGetSome
fonte
7

O código a seguir mostra o início e o cancelamento de uma solicitação do Ajax:

function libAjax(){
  var req;
  function start(){

  req =    $.ajax({
              url: '1.php',
              success: function(data){
                console.log(data)
              }
            });

  }

  function stop(){
    req.abort();
  }

  return {start:start,stop:stop}
}

var obj = libAjax();

 $(".go").click(function(){


  obj.start();


 })



 $(".stop").click(function(){

  obj.stop();


 })
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="button" class="go" value="GO!" >
   <input type="button" class="stop" value="STOP!" >
zloctb
fonte
5
Forneça uma explicação de como isso resolve a questão. Isso ajudará o OP e outros pesquisadores futuros.
SnareChops
5

Eu tive o problema de pesquisar e, uma vez que a página foi fechada, a pesquisa continuou, por isso, na minha causa, um usuário perderia uma atualização, pois um valor mysql estava sendo definido pelos próximos 50 segundos após o fechamento da página, mesmo que eu tenha matado a solicitação ajax, calculado, usar $ _SESSION para definir um var não será atualizado automaticamente na enquete até que termine e inicie um novo, então o que eu fiz foi definir um valor no meu banco de dados como 0 = offpage, enquanto estou sondagem eu consulto essa linha e retorno false; quando for 0, pois a consulta nas pesquisas obterá os valores atuais, obviamente ...

Espero que isso tenha ajudado

Marcus
fonte
5

E se xhr.abort(); causar o recarregamento da página,

Em seguida, você pode definir onreadystatechangeantes do cancelamento para impedir:

// ↓ prevent page reload by abort()
xhr.onreadystatechange = null;
// ↓ may cause page reload
xhr.abort();
vikyd
fonte
4

Compartilhei uma demonstração que demonstra como cancelar uma solicitação AJAX - se os dados não forem retornados do servidor dentro de um tempo de espera predefinido.

HTML:

<div id="info"></div>

CÓDIGO JS:

var isDataReceived= false, waitTime= 1000; 
$(function() {
    // Ajax request sent.
     var xhr= $.ajax({
      url: 'http://api.joind.in/v2.1/talks/10889',
      data: {
         format: 'json'
      },     
      dataType: 'jsonp',
      success: function(data) {      
        isDataReceived= true;
        $('#info').text(data.talks[0].talk_title);        
      },
      type: 'GET'
   });
   // Cancel ajax request if data is not loaded within 1sec.
   setTimeout(function(){
     if(!isDataReceived)
     xhr.abort();     
   },waitTime);   
});
ganesh
fonte
1
$.ajaxfornece uma timeoutopção para que você não precise escrever sua própria. Basta usar ..., data: {...}, timeout: waitTime,....
Bram Vanroy 06/07