224

I have a problem, when submitting a form all active ajax request fail, and that triggers error event.

How to stop all active ajax requests in jQuery without trigerring error event?

umpirsky
  • 9,902
  • 13
  • 71
  • 96

17 Answers17

281

Every time you create an ajax request you could use a variable to store it:

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

Then you can abort the request:

request.abort();

You could use an array keeping track of all pending ajax requests and abort them if necessary.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
180

The following snippet allows you to maintain a list (pool) of request and abort them all if needed. Best to place in the <HEAD> of your html, before any other AJAX calls are made.

<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>
SpYk3HH
  • 22,272
  • 11
  • 70
  • 81
mkmurray
  • 2,434
  • 3
  • 21
  • 22
  • 2
    @mkmurray - on initalization in IE8 I seem to get `Object doesn't support property or method 'indexOf'` ? I suspect it might be http://stackoverflow.com/a/2608601/181971 or maybe simply swap to http://stackoverflow.com/a/2608618/181971 ? – Tim May 09 '12 at 05:26
  • 3
    @grr is right, see his answer and check the docs for [ajaxSetup](http://api.jquery.com/jQuery.ajaxSetup/). – kzfabi Aug 13 '13 at 14:14
  • @Tim - as Steven suggested, instead of var index = $.xhrPool.indexOf(jqXHR); use: var index = $.inArray(jqXHR, $.xhrPool); – Christopher Jan 30 '14 at 09:14
  • 2
    @mkmurray: It shows TypeError: jqXHR.abort is not a function for me. :( – Shesha Mar 23 '16 at 06:52
  • There is a slight logical error in abortAll method, which is fixed here in this answer https://stackoverflow.com/a/45500874/1041341 – Sarin Jacob Sunny Aug 04 '17 at 08:48
  • 1
    btw, JQuery strongly advises against the use of $.ajaxSetup api.Which I feel is correct because it will effect ALL the ajax requests, even the ones used by plugins etc. This can lead to unwanted troubles. – A Nice Guy Jan 09 '18 at 07:00
125

Using ajaxSetup is not correct, as is noted on its doc page. It only sets up defaults, and if some requests override them there will be a mess.

I am way late to the party, but just for future reference if someone is looking for a solution to the same problem, here is my go at it, inspired by and largely identical to the previous answers, but more complete

// 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);
kzfabi
  • 2,065
  • 1
  • 22
  • 26
grr
  • 1,359
  • 1
  • 9
  • 4
  • How does one call the abort() method from other functions? – Stan James Sep 30 '15 at 21:28
  • abort is a function, not a method. you call it from within the same encapsulation normally, if you need to use it outside of the encapsulation you can remove the "var" before the function name and it will become a globally available funciton – Trey Nov 04 '15 at 15:26
  • Hi, could someone explain when r will be undefined? – Varun Mar 31 '20 at 10:34
  • Update 2023, don't use `onbeforeunload` either. Use page lifecycle event like `pagehide` and `visibilitychange` https://developer.chrome.com/blog/page-lifecycle-api/#observing-page-lifecycle-states-in-code – dhwang Aug 01 '23 at 16:15
35

Here's what I'm currently using to accomplish that.

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

Note: _.each of underscore.js is present, but obviously not necessary. I'm just lazy and I don't want to change it to $.each(). 8P

Andy Ferra
  • 769
  • 5
  • 8
  • 2
    I've got a slightly modified solution that works great that I am just about to post. – mkmurray Jan 12 '12 at 19:52
  • 9
    This leaks memory. `aboutAll` should remove the elements from the array. Plus when a request is finished, it should remove itself from the list. – Behrang Jun 04 '12 at 06:15
  • 7
    @BehrangSaeedzadeh You should've also posted an improved version then. – mattsven Aug 11 '13 at 06:30
  • ```$.xhrPool.abortAll = function () { $(this).each(function (i, jqXHR) { jqXHR.abort(); }); $.xhrPool.length = 0; })``` – dhwang Aug 01 '23 at 16:09
21

Give each xhr request a unique id and store the object reference in an object before sending. Delete the reference after an xhr request completes.

To cancel all request any time:

$.ajaxQ.abortAll();

Returns the unique ids of canceled request. Only for testing purposes.

Working function:

$.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;
    }
  };

})();

Returns an object with single function which can be used to add more functionality when required.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
refik
  • 458
  • 6
  • 11
21

I found it too easy for multiple requests.

step1: define a variable at top of page:

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

step2: set beforeSend in all ajax requests:

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

step3: use it wherever you required:

   $.each(xhrPool, function(idx, jqXHR) {
          jqXHR.abort();
    });
Dharmesh patel
  • 654
  • 1
  • 12
  • 23
6

I extended mkmurray and SpYk3HH answer above so that xhrPool.abortAll can abort all pending requests of a given url :

$.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;
});

Usage is same except that abortAll can now optionally accept a url as a parameter and will cancel only pending calls to that url

kofifus
  • 17,260
  • 17
  • 99
  • 173
5

I had some problems with andy's code, but it gave me some great ideas. First problem was that we should pop off any jqXHR objects that successfully complete. I also had to modify the abortAll function. Here is my final working code:

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

I didn't like the ajaxComplete() way of doing things. No matter how I tried to configure .ajaxSetup it did not work.

mahatmanich
  • 10,791
  • 5
  • 63
  • 82
the Hampster
  • 876
  • 1
  • 14
  • 21
  • 7
    I think you may be calling pop on the wrong request if they don't complete in a particular order? – jjmontes Nov 30 '11 at 13:34
  • 1
    Yes, you want to do slice instead of pop. I've got a slightly modified solution that I am about to post. – mkmurray Jan 12 '12 at 19:52
4

I have updated the code to make it works for me

$.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);
        }
    }
});
Steven
  • 59
  • 2
4

Throwing my hat in. Offers abort and remove methods against the xhrPool array, and is not prone to issues with ajaxSetup overrides.

/**
 * 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);
});
onassar
  • 3,313
  • 7
  • 36
  • 58
2

Make a pool of all ajax request and abort them.....

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++]);
    }
};
Zigri2612
  • 2,279
  • 21
  • 33
2
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));

whenever you want to abort all the ajax request, you just need call this line

Request.AbortAll()
Omid Matouri
  • 157
  • 5
1

Better to use independent code.....

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++]);
    }
};
Zigri2612
  • 2,279
  • 21
  • 33
0

Just as important: say you want to log off and you are generating new requests with timers: because session data is renewed with each new bootstrap (maybe you can tell I am talking Drupal, but this could be any site that uses sessions)... I had to go through all my scripts with a search and replace, cause I had a ton of stuff running in different cases: global variables at the top:

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');
}
GoodNews
  • 35
  • 7
0

Here is a copy past function that refreches all your ajax calls.
The fillCompteList() and fetchAll() must return ajax object :

function fillCompteList() {
   return $.ajax({
            url: 'www.somewhere.com' ,
            method: 'GET',
            success: function(res){
            ...
          });

And then use this

var xhrPool = [fillCompteList(inisial), fetchAll(params)] ;//old
function refrechAllUsing(SOME , params){
    xhrPool.forEach(function(request){
        request.abort();
    });
    xhrPool = [fillCompteList(SOME), fetchAll(params)]//new with other parameters
    Promise.all(xhrPool).then(() => {
        $('#loadding').undisplay();//remove the loadding screen 

    }).catch(() => {
        warning("Some problem happened");
        $('#loadding').undisplay();//remove the loadding screen 
    });
}
Yasser CHENIK
  • 370
  • 1
  • 6
  • 17
-3

Here's how to hook this up on any click (useful if your page is placing many AJAX calls and you're trying to navigate away).

$ ->
    $.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()
dB.
  • 4,700
  • 2
  • 46
  • 51
-4

There is a dummy solution I use it to abort all ajax requests. This solution is reload the whole page. This solution is good if you don't like to assign an ID to each ajax request, and if you make ajax requests inside for-loop. This will make sure all ajax requests are killed.

location.reload();
ASammour
  • 865
  • 9
  • 12