Detenga todas las solicitudes de ajax activas en jQuery


216

Tengo un problema, cuando envío un formulario, todas las solicitudes activas de ajax fallan, y eso desencadena un evento de error.

¿Cómo detener todas las solicitudes activas de ajax en jQuery sin un evento de error de trigerring?

Respuestas:


273

Cada vez que crea una solicitud ajax, puede usar una variable para almacenarla:

var request = $.ajax({
    type: 'POST',
    url: 'someurl',
    success: function(result){}
});

Entonces puedes abortar la solicitud:

request.abort();

Puede utilizar una matriz que rastrea todas las solicitudes pendientes de ajax y abortarlas si es necesario.


THXs, agregué una BANDERA porque usé varias solicitudes al mismo tiempo
jcho360

Aquí hay un ejemplo de trabajo simple: stackoverflow.com/a/42312101/3818394
Dharmesh patel

Tengo una llamada ajax en función, ¿cómo puedo cancelarla?
Kalariya_M

La variable debe declararse global para acceder desde otra función cuando hay una llamada ajax en curso. ejemplo: un proceso de carga de múltiples archivos.
Clain Dsilva

180

El siguiente fragmento le permite mantener una lista ( grupo ) de solicitudes y cancelarlas todas si es necesario. Lo mejor es colocarlo en <HEAD>su html, antes de realizar cualquier otra llamada AJAX.

<script type="text/javascript">
    $(function() {
        $.xhrPool = [];
        $.xhrPool.abortAll = function() {
            $(this).each(function(i, jqXHR) {   //  cycle through list of recorded connection
                jqXHR.abort();  //  aborts connection
                $.xhrPool.splice(i, 1); //  removes from list by index
            });
        }
        $.ajaxSetup({
            beforeSend: function(jqXHR) { $.xhrPool.push(jqXHR); }, //  annd connection to list
            complete: function(jqXHR) {
                var i = $.xhrPool.indexOf(jqXHR);   //  get index for current connection completed
                if (i > -1) $.xhrPool.splice(i, 1); //  removes from list by index
            }
        });
    })
</script>

2
@mkmurray - en la inicialización en IE8 parece que tengo Object doesn't support property or method 'indexOf'? Sospecho que podría ser stackoverflow.com/a/2608601/181971 o tal vez simplemente cambiar a stackoverflow.com/a/2608618/181971 ?
Tim

3
@grr tiene razón, vea su respuesta y verifique los documentos para ajaxSetup .
kzfabi

@Tim: como sugirió Steven, en lugar de var index = $ .xhrPool.indexOf (jqXHR); use: var index = $ .inArray (jqXHR, $ .xhrPool);
Christopher

1
@mkmurray: Muestra TypeError: jqXHR.abort no es una función para mí. :(
Shesha

Hay un ligero error lógico en el método abortAll, que se corrige aquí en esta respuesta stackoverflow.com/a/45500874/1041341
Sarin JS

122

El uso de ajaxSetup no es correcto , como se señala en su página de documentación. Solo configura los valores predeterminados, y si algunas solicitudes los anulan, habrá un desastre.

Llego tarde a la fiesta, pero solo para referencia futura si alguien está buscando una solución al mismo problema, aquí está mi intento, inspirado y en gran parte idéntico a las respuestas anteriores, pero más completo

// Automatically cancel unfinished ajax requests 
// when the user navigates elsewhere.
(function($) {
  var xhrPool = [];
  $(document).ajaxSend(function(e, jqXHR, options){
    xhrPool.push(jqXHR);
  });
  $(document).ajaxComplete(function(e, jqXHR, options) {
    xhrPool = $.grep(xhrPool, function(x){return x!=jqXHR});
  });
  var abort = function() {
    $.each(xhrPool, function(idx, jqXHR) {
      jqXHR.abort();
    });
  };

  var oldbeforeunload = window.onbeforeunload;
  window.onbeforeunload = function() {
    var r = oldbeforeunload ? oldbeforeunload() : undefined;
    if (r == undefined) {
      // only cancel requests if there is no prompt to stay on the page
      // if there is a prompt, it will likely give the requests enough time to finish
      abort();
    }
    return r;
  }
})(jQuery);

¿Cómo se llama al método abort () desde otras funciones?
Stan James

abortar es una función, no un método. normalmente lo llama desde dentro de la misma encapsulación, si necesita usarlo fuera de la encapsulación puede eliminar la "var" antes del nombre de la función y se convertirá en una función disponible globalmente
Trey

Hola, ¿alguien podría explicar cuándo r estará indefinido?
Varun

36

Esto es lo que estoy usando actualmente para lograr eso.

$.xhrPool = [];
$.xhrPool.abortAll = function() {
  _.each(this, function(jqXHR) {
    jqXHR.abort();
  });
};
$.ajaxSetup({
  beforeSend: function(jqXHR) {
    $.xhrPool.push(jqXHR);
  }
});

Nota: _.each of underscore.js está presente, pero obviamente no es necesario. Solo soy flojo y no quiero cambiarlo a $ .each (). 8P


2
Tengo una solución ligeramente modificada que funciona muy bien y que estoy a punto de publicar.
mkmurray

77
Esto pierde memoria. aboutAlldebería eliminar los elementos de la matriz. Además, cuando finaliza una solicitud, debe eliminarse de la lista.
Behrang Saeedzadeh

55
@BehrangSaeedzadeh También debería haber publicado una versión mejorada.
mattsven

19

Dele a cada solicitud xhr una identificación única y almacene la referencia del objeto en un objeto antes de enviarlo. Elimine la referencia después de que se complete una solicitud xhr.

Para cancelar toda solicitud en cualquier momento:

$.ajaxQ.abortAll();

Devuelve los identificadores únicos de la solicitud cancelada. Solo con fines de prueba.

Función de trabajo:

$.ajaxQ = (function(){
  var id = 0, Q = {};

  $(document).ajaxSend(function(e, jqx){
    jqx._id = ++id;
    Q[jqx._id] = jqx;
  });
  $(document).ajaxComplete(function(e, jqx){
    delete Q[jqx._id];
  });

  return {
    abortAll: function(){
      var r = [];
      $.each(Q, function(i, jqx){
        r.push(jqx._id);
        jqx.abort();
      });
      return r;
    }
  };

})();

Devuelve un objeto con una sola función que se puede usar para agregar más funcionalidad cuando sea necesario.


17

Lo encontré demasiado fácil para múltiples solicitudes.

Paso 1: define una variable en la parte superior de la página:

  xhrPool = []; // no need to use **var**

Paso 2: establecer antes de enviar en todas las solicitudes ajax:

  $.ajax({
   ...
   beforeSend: function (jqXHR, settings) {
        xhrPool.push(jqXHR);
    },
    ...

Paso 3: úsalo donde lo necesites:

   $.each(xhrPool, function(idx, jqXHR) {
          jqXHR.abort();
    });

Esto pierde memoria, al igual que stackoverflow.com/a/6618288/1772379 , y precisamente por las mismas razones.
Ben Johnson

Una forma realmente horrible de escribir JavaScript.
Ozil

1
puede estar al final, puede borrar / vaciar la matriz xhrPool
space earth

6

Extendí mkmurray y la respuesta SpYk3HH anterior para que xhrPool.abortAll pueda abortar todas las solicitudes pendientes de una URL dada :

$.xhrPool = [];
$.xhrPool.abortAll = function(url) {
    $(this).each(function(i, jqXHR) { //  cycle through list of recorded connection
        console.log('xhrPool.abortAll ' + jqXHR.requestURL);
        if (!url || url === jqXHR.requestURL) {
            jqXHR.abort(); //  aborts connection
            $.xhrPool.splice(i, 1); //  removes from list by index
        }
    });
};
$.ajaxSetup({
    beforeSend: function(jqXHR) {
        $.xhrPool.push(jqXHR); //  add connection to list
    },
    complete: function(jqXHR) {
        var i = $.xhrPool.indexOf(jqXHR); //  get index for current connection completed
        if (i > -1) $.xhrPool.splice(i, 1); //  removes from list by index
    }
});
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
    console.log('ajaxPrefilter ' + options.url);
    jqXHR.requestURL = options.url;
});

El uso es el mismo, excepto que abortAll ahora puede aceptar opcionalmente una url como parámetro y cancelará solo las llamadas pendientes a esa url


5

Tuve algunos problemas con el código de Andy, pero me dio algunas ideas geniales. El primer problema fue que deberíamos extraer cualquier objeto jqXHR que se complete con éxito. También tuve que modificar la función abortAll. Aquí está mi código de trabajo final:

$.xhrPool = [];
$.xhrPool.abortAll = function() {
            $(this).each(function(idx, jqXHR) {
                        jqXHR.abort();
                        });
};
$.ajaxSetup({
    beforeSend: function(jqXHR) {
            $.xhrPool.push(jqXHR);
            }
});
$(document).ajaxComplete(function() {
            $.xhrPool.pop();
            });

No me gustó la forma de hacer las cosas ajaxComplete (). No importa cómo intenté configurar .ajaxSetup, no funcionó.


77
¿Creo que puede estar llamando pop en la solicitud incorrecta si no se completan en un orden en particular?
jjmontes

1
Sí, quieres hacer una rebanada en lugar de pop. Tengo una solución ligeramente modificada que estoy a punto de publicar.
mkmurray

4

He actualizado el código para que funcione para mí.

$.xhrPool = [];
$.xhrPool.abortAll = function() {
    $(this).each(function(idx, jqXHR) {
        jqXHR.abort();
    });
    $(this).each(function(idx, jqXHR) {
        var index = $.inArray(jqXHR, $.xhrPool);
        if (index > -1) {
            $.xhrPool.splice(index, 1);
        }
    });
};

$.ajaxSetup({
    beforeSend: function(jqXHR) {
        $.xhrPool.push(jqXHR);
    },
    complete: function(jqXHR) {
        var index = $.inArray(jqXHR, $.xhrPool);
        if (index > -1) {
            $.xhrPool.splice(index, 1);
        }
    }
});

4

Lanzando mi sombrero. Ofertas aborty removemétodos contra la xhrPoolmatriz, y no es propenso a problemas con las ajaxSetupanulaciones.

/**
 * Ajax Request Pool
 * 
 * @author Oliver Nassar <onassar@gmail.com>
 * @see    http://stackoverflow.com/questions/1802936/stop-all-active-ajax-requests-in-jquery
 */
jQuery.xhrPool = [];

/**
 * jQuery.xhrPool.abortAll
 * 
 * Retrieves all the outbound requests from the array (since the array is going
 * to be modified as requests are aborted), and then loops over each of them to
 * perform the abortion. Doing so will trigger the ajaxComplete event against
 * the document, which will remove the request from the pool-array.
 * 
 * @access public
 * @return void
 */
jQuery.xhrPool.abortAll = function() {
    var requests = [];
    for (var index in this) {
        if (isFinite(index) === true) {
            requests.push(this[index]);
        }
    }
    for (index in requests) {
        requests[index].abort();
    }
};

/**
 * jQuery.xhrPool.remove
 * 
 * Loops over the requests, removes it once (and if) found, and then breaks out
 * of the loop (since nothing else to do).
 * 
 * @access public
 * @param  Object jqXHR
 * @return void
 */
jQuery.xhrPool.remove = function(jqXHR) {
    for (var index in this) {
        if (this[index] === jqXHR) {
            jQuery.xhrPool.splice(index, 1);
            break;
        }
    }
};

/**
 * Below events are attached to the document rather than defined the ajaxSetup
 * to prevent possibly being overridden elsewhere (presumably by accident).
 */
$(document).ajaxSend(function(event, jqXHR, options) {
    jQuery.xhrPool.push(jqXHR);
});
$(document).ajaxComplete(function(event, jqXHR, options) {
    jQuery.xhrPool.remove(jqXHR);
});

2

Haga un grupo de todas las solicitudes de ajax y abortarlas .....

var xhrQueue = []; 

$(document).ajaxSend(function(event,jqxhr,settings){
    xhrQueue.push(jqxhr); //alert(settings.url);
});

$(document).ajaxComplete(function(event,jqxhr,settings){
    var i;   
    if((i=$.inArray(jqxhr,xhrQueue)) > -1){
        xhrQueue.splice(i,1); //alert("C:"+settings.url);
    }
});

ajaxAbort = function (){  //alert("abortStart");
    var i=0;
    while(xhrQueue.length){ 
        xhrQueue[i++] .abort(); //alert(i+":"+xhrQueue[i++]);
    }
};

1

Mejor usar código independiente .....

var xhrQueue = []; 

$(document).ajaxSend(function(event,jqxhr,settings){
    xhrQueue.push(jqxhr); //alert(settings.url);
});

$(document).ajaxComplete(function(event,jqxhr,settings){
    var i;   
    if((i=$.inArray(jqxhr,xhrQueue)) > -1){
        xhrQueue.splice(i,1); //alert("C:"+settings.url);
    }
});

ajaxAbort = function (){  //alert("abortStart");
    var i=0;
    while(xhrQueue.length){ 
        xhrQueue[i++] .abort(); //alert(i+":"+xhrQueue[i++]);
    }
};

0

Igual de importante: digamos que desea cerrar sesión y está generando nuevas solicitudes con temporizadores: porque los datos de la sesión se renuevan con cada arranque nuevo (tal vez pueda decir que estoy hablando de Drupal, pero este podría ser cualquier sitio que use sesiones). Tuve que revisar todos mis scripts con una búsqueda y reemplazo, porque tenía un montón de cosas ejecutándose en diferentes casos: variables globales en la parte superior:

var ajReq = [];
var canAj = true;
function abort_all(){
 for(x in ajReq){
    ajReq[x].abort();
    ajReq.splice(x, 1)
 }
 canAj = false;
}
function rmvReq(ranNum){
 var temp = [];
 var i = 0;
 for(x in ajReq){
    if(x == ranNum){
     ajReq[x].abort();
     ajReq.splice(x, 1);
    }
    i++;
 }
}
function randReqIndx(){
 if(!canAj){ return 0; }
 return Math.random()*1000;
}
function getReqIndx(){
 var ranNum;
 if(ajReq.length){
    while(!ranNum){
     ranNum = randReqIndx();
     for(x in ajReq){
    if(x===ranNum){
     ranNum = null;
    }
     }
    }
    return ranMum;
 }
 return randReqIndx();
}
$(document).ready(function(){
 $("a").each(function(){
    if($(this).attr('href').indexOf('/logout')!=-1){          
     $(this).click(function(){
    abort_all();                 
     });
    }
 })
});
// Then in all of my scripts I wrapped my ajax calls... If anyone has a suggestion for a 
    // global way to do this, please post
var reqIndx = getReqIndx();
if(reqIndx!=0){
ajReq[reqIndx] = $.post(ajax, { 'action': 'update_quantities', iids:iidstr, qtys:qtystr },  
function(data){
 //..do stuff
 rmvReq(reqIndx);
 },'json');
}

0
var Request = {
    List: [],
    AbortAll: function () {
        var _self = this;
        $.each(_self.List, (i, v) => {
            v.abort();
        });
    }
}
var settings = {
    "url": "http://localhost",
    success: function (resp) {
        console.log(resp)
    }
}

Request.List.push($.ajax(settings));

cada vez que desee cancelar toda la solicitud de ajax, solo necesita llamar a esta línea

Request.AbortAll()

-2

Hay una solución ficticia que la uso para cancelar todas las solicitudes de ajax. Esta solución es volver a cargar toda la página. Esta solución es buena si no le gusta asignar una ID a cada solicitud ajax, y si realiza solicitudes ajax dentro de for-loop. Esto asegurará que se eliminen todas las solicitudes de ajax.

location.reload();

-3

Aquí le mostramos cómo conectar esto con cualquier clic (útil si su página está haciendo muchas llamadas AJAX y está tratando de navegar).

$ ->
    $.xhrPool = [];

$(document).ajaxSend (e, jqXHR, options) ->
    $.xhrPool.push(jqXHR)

$(document).ajaxComplete (e, jqXHR, options) ->
    $.xhrPool = $.grep($.xhrPool, (x) -> return x != jqXHR);

$(document).delegate 'a', 'click', ->
    while (request = $.xhrPool.pop())
      request.abort()
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.