Wednesday, December 19, 2007

Aborting Ajax requests (for prototype.js)

Sometimes your slick Ajax application may have submitted a request that you no longer care about and want to abort. For example, perhaps you've implemented a type-ahead feature and the user has now typed another character prior to the first results returning. Or perhaps you fetch values for other parts of a form based on user selections and the user changed their choice prior to the first response returning.

I was surprised to see that the prototype.js library does not include an abort method for its Ajax.Request object. So, here's my implementation of Ajax.Request.abort():

/**
* Ajax.Request.abort
* extend the prototype.js Ajax.Request object so that it supports an abort method
*/
Ajax.Request.prototype.abort = function() {
// prevent and state change callbacks from being issued
this.transport.onreadystatechange = Prototype.emptyFunction;
// abort the XHR
this.transport.abort();
// update the request counter
Ajax.activeRequestCount--;
};


To use this function, you need to keep a handle on the Ajax request you want to abort. So, somewhere in you code you'll have something like:

var myAjaxRequest = new Ajax.Request(requestUrl, {[request options]});


If you want to abort that request, simply call:


myAjaxRequest.abort();


Update - Feb. 22, 2008:

It was pointed out in the comments that the Ajax.activeRequestCount can occasionally become negative. I have been able to replicate that situation, while at the same time confirming that it does not consistently happen. This leads me to believe that it's most likely a timing issue such as the abort is issued after the response has already started to be received and/or processed so that both the response processing decrements the counter as well as the abort.

My personal work-around for this is to add these lines to the end of my onComplete handler:

if (Ajax.activeRequestCount < 0) {
Ajax.activeRequestCount = 0;
}

I don't want to remove the counter decrement from the abort function or else cleanly aborted requests will leave the activeRequestCount > 0 when there are no real outstanding requests.

If anyone has a better solution, I'd be interested to hear from you.

14 comments:

hetman said...

Cheers mate.

This sseems to have resolved an issue I was having when only calling: this.transport.abort();

Maybe you can shed some light on why the absence of the first line was preventing a concurrent Element.update() from executing?

hetman said...

Cheers mate.

This sseems to have resolved an issue I was having when only calling: this.transport.abort();

Maybe you can shed some light on why the absence of the first line was preventing a concurrent Element.update() from executing?

Sara Hedin said...

Great article and thanks to Steven for helping me implement it!

This should really be included in the official prototype.js.

Anonymous said...

Thanks so much Steve. Please excuse my newbieness. How do i integrate your abort code with my javascript module? Do I paste it in prototype.js or in my own module?

Thanks so much

Steven Pothoven said...

I would recommend that you just add it to your own JavaScript code sometime after you've included the prototype.js code.

I would not recommend you adding to the prototype.js file (or any other 3rd party library) as it then complicates the process of upgrading to new versions since you need to remember to copy over all the additions you have made.

maohao said...

Hi,

Thanks for the code first of all.

I seem to have some issue with Ajax.activeRequestCount when using it in conjunction with Ajax.Responders. The activeRequestCount became negative. I commented out the "activeRequestCount--" line and it now seems fine.

Here is my code for the responders:

Ajax.Responders.register({
onCreate: function() {
//alert("# of request is "+Ajax.activeRequestCount);
if($('indicator1') && Ajax.activeRequestCount>0)
$('indicator1').show();
},
onComplete: function() {
if($('indicator1') && Ajax.activeRequestCount==0)
$('indicator1').hide();
},
onUninitialized: function() {
if($('indicator1') && Ajax.activeRequestCount==0)
$('indicator1').hide();
}
});

maohao said...

Hi,

First of all thanks.

I seem to get negative Ajax.activeRequestCount when using the code in conjunction with Ajax.Responders. I commented out the line "Ajax.activeRequestCount--;" and now it seems fine. Here is my code if that matters at all:

Ajax.Responders.register({
onCreate: function() {
//alert("# of request is "+Ajax.activeRequestCount);
if($('indicator1') && Ajax.activeRequestCount>0)
$('indicator1').show();
},
onComplete: function() {
if($('indicator1') && Ajax.activeRequestCount==0)
$('indicator1').hide();
},
onUninitialized: function() {
if($('indicator1') && Ajax.activeRequestCount==0)
$('indicator1').hide();
}
});

Steven Pothoven said...

I use essentially the same code and have not encountered any issues with negative values.

Are you aborting a request that has already completed? In my response handling functions, I always set my saved Ajax object to null, and check the object before calling abort in it.

Anonymous said...

Thanks you verymuch. This is very usefull for me.

Carl said...

Its very likely that this hasn't been included in prototype because the abort method in XHR isn't supported by IE<7. Great addition though and when combined with a little object detection there's no reason it cant be implemented as a progressive enhancement

Juissi said...

I'm using firefox and tried your solution on my project. When I abort ajax request code seems to work fine and nothing is done after aborting, but my firebug still shows there's a request running and it won't never stop. Is this only firebug's problem? I don't know but I decided to ask you if you have any knowledge about this.

Anonymous said...

Thanks for posting this. I have been looking for something like this for a long time. I too was surprised not to find built in support as a part of Prototype's AJAX library.

Anonymous said...

Hey are you a professional journalist? This article is very well written, as compared to most other blogs i saw today….
anyhow thanks for the good read!

Daniel Gandra said...

Excellent article! I don't understand what are waiting Prototype to include this change on their code.

About the abort problem in some situations where the Ajax.activeRequestCount becomes negative and to avoid future problems when updating prototype as it was commented before, I did the following:

a) create a prototype_extended.js file and include it after including prototype.js to take effect.

b) put the following code:
Ajax.Responders.register({
onComplete: function(){
//alert ('inside oncomplete code section...');
Ajax.activeRequestCount--;

if (Ajax.activeRequestCount < 0) {
Ajax.activeRequestCount = 0;
}
}
});