Como cancelar / abortar a solicitação do jQuery AJAX?

244

Eu tenho uma solicitação AJAX que será feita a cada 5 segundos. Mas o problema está antes da solicitação AJAX, se a solicitação anterior não for concluída, eu tenho que abortar essa solicitação e fazer uma nova solicitação.

Meu código é algo como isto, como resolver esse problema?

$(document).ready(
    var fn = function(){
        $.ajax({
            url: 'ajax/progress.ftl',
            success: function(data) {
                //do something
            }
        });
    };

    var interval = setInterval(fn, 500);
);
user556673
fonte
5
Sua pergunta indica que a solicitação deve ocorrer a cada 5 segundos, mas o código usa 500 ms = 0,5 s.
26613

Respostas:

355

O método jquery ajax retorna um objeto XMLHttpRequest . Você pode usar esse objeto para cancelar a solicitação.

O XMLHttpRequest possui um método de cancelamento , que cancela a solicitação, mas se a solicitação já tiver sido enviada ao servidor, o servidor processará a solicitação, mesmo que a solicitação seja cancelada, mas o cliente não esperará / manipulará a resposta.

O objeto xhr também contém um readyState que contém o estado da solicitação (UNSENT-0, OPENED-1, HEADERS_RECEIVED-2, LOADING-3 e DONE-4). podemos usar isso para verificar se a solicitação anterior foi concluída.

$(document).ready(
    var xhr;

    var fn = function(){
        if(xhr && xhr.readyState != 4){
            xhr.abort();
        }
        xhr = $.ajax({
            url: 'ajax/progress.ftl',
            success: function(data) {
                //do something
            }
        });
    };

    var interval = setInterval(fn, 500);
);
Arun P Johny
fonte
60
@romkyns As diferenças é a propriedade readystateacima e readyStateabaixo, o carácter sé maiúscula no jQuery 1,5
Arun P Johny
Eu queria saber o que acontece se o readyState for "LOADING-3" e abortarmos? Os dados são enviados para o servidor e são processados ​​ou não. Não saberemos ...: S
inf3rno 13/06
7
Aliás, acho que a verificação do readyState é desnecessária, pois é adiada e, uma vez resolvida ou rejeitada, ela não executará as outras. Então if (xhr) xhr.abort();vai funcionar muito bem também.
Ciantic
2
O W3 nunca usou não readystateem maiúsculas em sua documentação. Ele estava sempre em maiúsculas readyStatee o jquery (antes ou depois da 1.5) correspondia corretamente à especificação w3.
Arashsoft
@ArunPJohny, você pode pls esta questão stackoverflow.com/questions/49801146/...
Krishna Jonnalagadda
6

Quando você faz uma solicitação a um servidor, verifique se um progresso não é nulo (ou buscando esses dados) primeiro. Se estiver buscando dados, aborte a solicitação anterior e inicie a nova.

var progress = null

function fn () {    
    if (progress) {
        progress.abort();
    }
    progress = $.ajax('ajax/progress.ftl', {
        success: function(data) {
            //do something
            progress = null;
        }
    });
}
Taz
fonte
5

Eu sei que isso pode ser um pouco tarde, mas eu experimentei problemas semelhantes em que chamar o método abort realmente não abortou a solicitação. em vez disso, o navegador ainda estava esperando por uma resposta que ele nunca usa. esse código resolveu esse problema.

 try {
        xhr.onreadystatechange = null;
        xhr.abort();
} catch (e) {}
Lyon
fonte
1

Por que você deve abortar a solicitação?

Se cada solicitação demorar mais de cinco segundos, o que acontecerá?

Você não deve abortar a solicitação se o parâmetro que passa com a solicitação não estiver sendo alterado. por exemplo: - a solicitação é para recuperar os dados da notificação. Nessas situações, a abordagem legal é que defina uma nova solicitação somente após a conclusão da solicitação Ajax anterior.

$(document).ready(

    var fn = function(){

        $.ajax({
            url: 'ajax/progress.ftl',
            success: function(data) {
                //do something
            },

            complete: function(){setTimeout(fn, 500);}
        });
    };

     var interval = setTimeout(fn, 500);

);
Habeeb Perwad
fonte
5
Esta é uma má ideia. Ao iniciar uma nova chamada, você não deseja que o evento completo seja acionado da chamada anterior. Você pode obter resultados muito estranhos com essa abordagem.
Webberig 07/04
1

Você pode usar o jquery-validate.js. A seguir está o trecho de código de jquery-validate.js.

// ajax mode: abort
// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()

var pendingRequests = {},
    ajax;
// Use a prefilter if available (1.5+)
if ( $.ajaxPrefilter ) {
    $.ajaxPrefilter(function( settings, _, xhr ) {
        var port = settings.port;
        if ( settings.mode === "abort" ) {
            if ( pendingRequests[port] ) {
                pendingRequests[port].abort();
            }
            pendingRequests[port] = xhr;
        }
    });
} else {
    // Proxy ajax
    ajax = $.ajax;
    $.ajax = function( settings ) {
        var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
            port = ( "port" in settings ? settings : $.ajaxSettings ).port;
        if ( mode === "abort" ) {
            if ( pendingRequests[port] ) {
                pendingRequests[port].abort();
            }
            pendingRequests[port] = ajax.apply(this, arguments);
            return pendingRequests[port];
        }
        return ajax.apply(this, arguments);
    };
}

Para que você só precise definir o modo de parâmetro para abortar quando estiver fazendo uma solicitação ajax.

Ref: https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.14.0/jquery.validate.js

zawhtut
fonte
0

jQuery: use isso como ponto de partida - como inspiração. Eu resolvi assim: (essa não é uma solução perfeita, apenas anula a última instância e é o código WIP)

var singleAjax = function singleAjax_constructor(url, params) {
    // remember last jQuery's get request
    if (this.lastInstance) {
        this.lastInstance.abort();  // triggers .always() and .fail()
        this.lastInstance = false;
    }

    // how to use Deferred : http://api.jquery.com/category/deferred-object/
    var $def = new $.Deferred();

    // pass the deferrer's request handlers into the get response handlers
    this.lastInstance = $.get(url, params)
        .fail($def.reject)         // triggers .always() and .fail()
        .success($def.resolve);    // triggers .always() and .done()

    // return the deferrer's "control object", the promise object
    return $def.promise();
}


// initiate first call
singleAjax('/ajax.php', {a: 1, b: 2})
    .always(function(a,b,c) {console && console.log(a,b,c);});

// second call kills first one
singleAjax('/ajax.php', {a: 1, b: 2})
    .always(function(a,b,c) {console && console.log(a,b,c);});
    // here you might use .always() .fail() .success() etc.
BananaAcid
fonte
0

Crie uma função para chamar sua API. Dentro dessa função, definimos solicitação callApiRequest = $.get(...- mesmo que seja uma definição de variável, a solicitação é chamada imediatamente, mas agora temos a solicitação definida como uma variável. Antes que a solicitação seja chamada, verificamos se nossa variável está definida typeof(callApiRequest) != 'undefined'e também se está pendente suggestCategoryRequest.state() == 'pending'- se as duas forem verdadeiras, é .abort()a solicitação que impedirá a execução do retorno de chamada com êxito.

// We need to wrap the call in a function
callApi = function () {

    //check if request is defined, and status pending
    if (typeof(callApiRequest) != 'undefined'
        && suggestCategoryRequest.state() == 'pending') {

        //abort request
        callApiRequest.abort()

    }

    //define and make request
    callApiRequest = $.get("https://example.com", function (data) {

        data = JSON.parse(data); //optional (for JSON data format)
        //success callback

    });
}

Seu servidor / API pode não suportar a interrupção da solicitação (e se a API já tiver executado algum código?), Mas o retorno de chamada javascript não será acionado. Isso é útil quando, por exemplo, você está fornecendo sugestões de entrada para um usuário, como entrada de hashtags.

Você pode estender ainda mais essa função adicionando a definição de retorno de chamada de erro - o que deve acontecer se a solicitação for abortada.

Caso de uso comum para esse snippet seria uma entrada de texto que é acionada no keypressevento. Você pode usar um tempo limite para impedir o envio de (algumas) solicitações que você precisará cancelar .abort().

mate.gwozdz
fonte
-7

Você também deve verificar o readyState 0. Porque, quando você usa xhr.abort (), essa função define readyState como 0 neste objeto, e sua verificação if será sempre verdadeira - readyState! = 4

$(document).ready(
    var xhr;

    var fn = function(){
        if(xhr && xhr.readyState != 4 && xhr.readyState != 0){
            xhr.abort();
        }
        xhr = $.ajax({
            url: 'ajax/progress.ftl',
            success: function(data) {
                //do something
            }
        });
    };

    var interval = setInterval(fn, 500);
); 
programador
fonte
2
Você copiou e colou a resposta de Arun? Porque a probabilidade de dois blocos de código como esse sendo exatamente o mesmo, espaçamento e tudo ...
@ user2700923 A publicação dele não é exatamente a mesma. Seu argumento é que também devemos verificar o readyState 0. No entanto, isso teria sido mais apropriado como um comentário à resposta de Arun.
Stefan