¿Cómo cancelar / cancelar la solicitud de jQuery AJAX?

244

Tengo una solicitud AJAX que se realizará cada 5 segundos. Pero el problema es antes de la solicitud AJAX, si la solicitud anterior no se completa, tengo que cancelar esa solicitud y hacer una nueva solicitud.

Mi código es algo como esto, ¿cómo resolver este problema?

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

    var interval = setInterval(fn, 500);
);
usuario556673
fuente
55
Su pregunta indica que la solicitud debe realizarse cada 5 segundos, pero el código usa 500 ms = 0.5 s.
geon

Respuestas:

355

El método jquery ajax devuelve un objeto XMLHttpRequest . Puede usar este objeto para cancelar la solicitud.

El XMLHttpRequest tiene un aborto método, que cancela la solicitud, pero si el pedido ya ha sido enviado al servidor a continuación, el servidor procesará la solicitud , incluso si abortar la solicitud, pero el cliente no esperará a / manejar la respuesta.

El objeto xhr también contiene readyState que contiene el estado de la solicitud (UNSENT-0, OPENED-1, HEADERS_RECEIVED-2, LOADING-3 y DONE-4). podemos usar esto para verificar si se completó la solicitud anterior.

$(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
fuente
6060
@romkyns La diferencia es la propiedad readystatearriba y readyStateabajo, el personaje sse escribe con mayúscula en jQuery 1.5
Arun P Johny
Me preguntaba qué sucede si readyState es "LOADING-3" y abortamos. Los datos se envían al servidor y se procesan o no. No sabremos ...: S
inf3rno
77
Por cierto, creo que la comprobación readyState es innecesaria, ya que se aplaza y una vez que se resuelve o se rechaza, no ejecutará las demás. Entonces if (xhr) xhr.abort();funcionará bien también.
Ciantic
2
W3 nunca utilizado no capitalizado readystateen su documentación. Siempre estaba en mayúscula readyStatey jquery (antes o después de 1.5) coincide correctamente con la especificación w3.
Arashsoft
@ArunPJohny, ¿puedes hacer esta pregunta stackoverflow.com/questions/49801146/…
Krishna Jonnalagadda
6

Cuando realice una solicitud a un servidor, haga que verifique si un progreso no es nulo (o si obtiene esos datos) primero. Si está obteniendo datos, cancele la solicitud anterior e inicie la nueva.

var progress = null

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

Sé que esto podría ser un poco tarde, pero tengo problemas similares en los que llamar al método de aborto realmente no canceló la solicitud. en cambio, el navegador todavía estaba esperando una respuesta que nunca usa. Este código resolvió ese problema.

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

¿Por qué deberías abortar la solicitud?

Si cada solicitud lleva más de cinco segundos, ¿qué sucederá?

No debe cancelar la solicitud si el parámetro que pasa con la solicitud no está cambiando. por ejemplo: - la solicitud es para recuperar los datos de notificación. En tales situaciones, el buen enfoque es establecer una nueva solicitud solo después de completar la solicitud anterior de Ajax.

$(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
fuente
55
Esta es una mala idea. Al iniciar una nueva llamada, no desea que se active el evento completo de la llamada anterior. Puede obtener resultados muy extraños con este enfoque.
Webberig
1

Puede usar jquery-validate.js. El siguiente es el fragmento 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);
    };
}

De modo que solo necesita establecer el modo de parámetro para abortar cuando realiza una solicitud ajax.

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

zawhtut
fuente
0

jQuery: use esto como punto de partida, como inspiración. Lo resolví así: (esta no es una solución perfecta, simplemente aborta la última instancia y es 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.
Plátano Ácido
fuente
0

Crea una función para llamar a tu API. Dentro de esta función definimos solicitud callApiRequest = $.get(..., aunque esta es una definición de una variable, la solicitud se llama de inmediato, pero ahora tenemos la solicitud definida como una variable. Antes de llamar a la solicitud, verificamos si nuestra variable está definida typeof(callApiRequest) != 'undefined'y también si está pendiente suggestCategoryRequest.state() == 'pending'; si ambas son verdaderas, hacemos .abort()la solicitud, lo que evitará que se ejecute la devolución de llamada exitosa.

// 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

    });
}

Es posible que su servidor / API no admita la cancelación de la solicitud (¿qué sucede si la API ya ejecutó algún código?), Pero la devolución de llamada de JavaScript no se activará. Esto es útil cuando, por ejemplo, está proporcionando sugerencias de entrada a un usuario, como la entrada de hashtags.

Puede ampliar aún más esta función agregando la definición de devolución de llamada de error, lo que debería suceder si se cancela la solicitud.

El caso de uso común para este fragmento sería una entrada de texto que se activa en keypresscaso de evento. Puede usar un tiempo de espera para evitar el envío de (algunas de) solicitudes que deberá cancelar .abort().

mate.gwozdz
fuente
-7

También debe verificar readyState 0. Porque cuando usa xhr.abort () esta función establece readyState en 0 en este objeto, y su comprobación if siempre será verdadera - 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
fuente
2
¿Copiaste y pegaste la respuesta de Arun? Porque la probabilidad de que dos bloques de código como ese sean exactamente iguales, espaciado y todo ...
@ user2700923 Su publicación no es exactamente la misma. Su punto es que también deberíamos verificar readyState 0. Sin embargo, esto habría sido más apropiado como comentario a la respuesta de Arun.
Stefan