Let's consider a fairly trivial, but probably typical, Ajax-based application. I've got a series of buttons:

Each button, when clicked, hits a service on my application server and fetches some data. In my case, just a simple name:

The code for this is rather simple. (And note - for the purposes of this blog entry I'm keeping things very simple and including my JavaScript in the HTML page. Please keep your HTML and JavaScript in different files!)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
</head>
<body>
<button data-prodid="1" class="loadButton">Load One</button>
<button data-prodid="2" class="loadButton">Load Two</button>
<div id="resultDiv"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script>
$(document).ready(function() {
$result = $("#resultDiv");
$(".loadButton").on("click", function(e) {
var thisId = $(this).data("prodid");
console.log("going to load product id "+thisId);
$result.text("");
$.getJSON("service.cfc?method=getData",{id:thisId}, function(res) {
console.log("back with "+JSON.stringify(res));
$result.text("Product "+res.name);
});
});
});
</script>
</body>
</html>
I assume this makes sense to everyone as it is pretty boiler-plate Ajax with jQuery, but if it doesn't, just chime in below in a comment. Ok, so this works, but we have a small problem. What happens in the user clicks both buttons at nearly the same time? Well, you would probably say the last one wins, right? But are you sure? What if something goes wrong (database gremlin - always blame the database) and the last hit is the first one to return?

What you can see (hopefully - still kinda new at making animated gifs) is that the user clicks the first button, then the second, and sees first the result from the second button and then the first one flashes in.
Now to be fair, you could just blame the user. I'm all for blaming the user. But what are some ways we can prevent this from happening?
One strategy is to disable all the buttons that call this particular Ajax request until the request has completed. Let's look at that version.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
</head>
<body>
<button data-prodid="1" class="loadButton">Load One</button>
<button data-prodid="2" class="loadButton">Load Two</button>
<div id="resultDiv"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script>
$(document).ready(function() {
$result = $("#resultDiv");
$(".loadButton").on("click", function(e) {
//disable the rest
$(".loadButton").attr("disabled","disabled");
var thisId = $(this).data("prodid");
console.log("going to load product id "+thisId);
$result.text("Loading info...");
$.getJSON("service.cfc?method=getData",{id:thisId}, function(res) {
console.log("back with "+JSON.stringify(res));
$(".loadButton").removeAttr("disabled");
$result.text("Product "+res.name);
});
});
});
</script>
</body>
</html>
I've added a simple call to disable all the buttons based on class. I then simple remove that attribute when the Ajax request is done. Furthermore, I also include some text to let the user know that - yes - something is happening - and maybe you should just calm the heck down and wait for it. The result makes it more obvious that something is happening and actively prevents the user from clicking the other buttons.

Another strategy would be to actually kill the existing Ajax request. This is rather simple. The native XHR object has an abort method that will kill it, and jQuery's Ajax methods returns a wrapped XHR object that gives us access to the same method.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
</head>
<body>
<button data-prodid="1" class="loadButton">Load One</button>
<button data-prodid="2" class="loadButton">Load Two</button>
<div id="resultDiv"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script>
$(document).ready(function() {
$result = $("#resultDiv");
var xhr;
var active=false;
$(".loadButton").on("click", function(e) {
var thisId = $(this).data("prodid");
console.log("going to load product id "+thisId);
$result.text("Loading info...");
if(active) { console.log("killing active"); xhr.abort(); }
active=true;
xhr = $.getJSON("service.cfc?method=getData",{id:thisId}, function(res) {
console.log("back with "+JSON.stringify(res));
$result.text("Product "+res.name);
active=false;
});
});
});
</script>
</body>
</html>
I use two variables, xhr and active, so that I can track active xhr requests. There are other ways to track the status of the XHR object - for example, via readyState - but a simple flag seemed to work best. Obviously you could do it differently but the main idea ("If active, kill it"), provides an alternative to the first method.
When using this, you can actually see the requests killed in dev tools:

Any comments on this? How are you handling this yourself in your Ajax-based applications?
p.s. As a quick aside, Brian Rinaldi shared with me a cool little UI library that turns buttons themselves into loading indicators: Ladda
Archived Comments
A user found an issue with multiple AJAX calls in our SPA. We ended up adopting the xhr.abort() approach.
I've used the BlockUI plugin. After including the library, I just use this default script:
$(document).ajaxStart($.blockUI).ajaxStop($.unblockUI);
then do my ajax calls as needed;
A third solution is to only have one ajax call in-flight/active, and queue any further requests and send the requests after you get the first response (also makes webserver coding simpler because there can be no race conditions if state modified for that user).
Ideally requests need to be consolidated together (not very RESTful though).
If deleting the previous message, only delete messages of the same kind.
For example, desktop keyboard up key/down key row selection on a master grid with a child form:
1. Send request of selected row number immediately to give fast response
2. If lots of down presses queued, only need to keep last message.
3. Disable child form while waiting for response.
4. Use a whole page disabling spinner when user saves.
You can simplify that a little more actually. You don't need the active variable, or check the ready state - all you need to do is
if (xhr) xhr.abort();
Usually what I do in those situations is send a token with the request that gets echo'd back with the response and if the response's token isn't the latest token i've sent then I ignore the response. I usually use a millisecond unix timestamp for the token but anything unique for that page request works fine.
but I like the idea of cancelling the ajax request. On mobile that could make a difference if you had a lot of this going on.
You could also use promises: when you return a Promise from a then callback, the next then waits on it to resolve. So if you use 1 Promise than you can synchonize tha calls and their result.
See the GitHub Gist:
https://gist.github.com/mic...
The response of getOne always returns after 10 milliseconds, and the response of getTwo always returns after 3000 milliseconds. If you call getTwo first and then immediately getOne, you still get the response of getOne after the response of getTwo.
Another option is to debounce click event callback. With this implemented you start sending AJAX request after some (short, about 200-300ms) time. That way you could create idiot-proof interrfaces ;)
What about adding button events to an intermediate array? Then you can observe that and pop the 1st or successful result off the "stack"
Fast and cheap. Is that too simplistic?
You can use the XHR
.readyState
property instead of theactive
variable:😁
During a typical
$.getJSON
call, you get a bunch of1
s and then a single4
at the end: http://jsbin.com/wayake/edi...You know I tried that - and oddly it didn't seem consistent for me - but I was probably just doing it wrong.
That seems more complex than simplistic. :) But I'd like to see that - can you share a JSBin?
That wouldn't help the main issue though. If the 1st click takes 3 seconds to work, and the second takes 0.5 seconds, we'd still have the same problem.
Pretty cool!
I don't agree. I believe that after such a long time (3 sec) a user might want to see diferrent results so it shouldn't be an issue if the AJAX responses are quick.
But that's the problem I'm trying to solve here. User clicks 1, gets tired of waiting, clicks 2, sees the result, then sees 1's result come in, which imo is bad.
If an app is permitting several requests for the same token without checking for a response, then it creates the premise for DoS attacks.
Hence, I'd say you're trying to solve a problem that shouldn't exist in the first place.
Otherwise, allowing the user to make several AJAX calls, each regarding different tokens, and the user getting the responses in a different order, that's probably not something you should have to synchronize.
I wouldn't say this causes an opening for DoS. I mean shoot, even in my "fixed" version, all I need to do is copy the URL, open a new tab, and go crazy with reloads. By me fixing it, it has slowed down my time to get to the attack by about 30 seconds.
"I mean shoot, even in my "fixed" version, all I need to do is copy the URL, open a new tab, and go crazy with reloads."
It's something different. It's the equivalent of multiple users asking the same resource for the same token. It's normal operation mode. It has to do with the server load balance.
Your scenario, if I understood it correctly, is the equivalent of a single user being able to ask the same resource for the same token continuously. The fact that such a thing is possible in the first place, that's an issue. You should not have to "fix" this, "it" should not be possible to begin with.
Finally, the same user being able to ask the same resource for multiple tokens, that's also normal operation mode, as long as one token is fully resolved, one way or the other, before being able to ask for it again.
My point is, you're approaching this as a enhancement, I think it should be a requirement.
Maybe I don't understand you then. Given that URL X represents "info on product 1", how do you propose to stop me from requesting that N times a second? You can't - unless you do something on the server to block my traffic.
Exactly my point: server and client mechanisms to stop unsatisfied requests from being issued over and over again under the same identical parameters are not optional, these are solid requirements from the get go.
I still don't think I'm understanding you. Are you saying you shouldn't do something on the client to provide a better experience because it doesn't stop a potentially DDOS attack? To me - that doesn't make sense. I think the changes described above help provide a better user experience, which is a good thing. Do they prevent a DDOS? No. Does this blog entry discuss DDOS? No. Is that an important topic? Sure - but it isn't relevant to this entry nor does it imply that what I propose here shouldn't be done. Imo, it should.
Let's recap.
You start by proposing a user can make parallel requests and get responses in a different order. For me this is perfectly fine, if we're talking about different tokens. Do you see an issue so far?
The point where I agree is when you identify a problem with the user (EDIT: user session ) being able to ask twice or more times for the same token regardless of the response loop back.
The point where I disagree is that this problem should not exist, to be fixed, in production. Beside possibly circumventing things like data correctness for the user, it's also a DoS vector.
But the whole point of this article is that the user is requesting -2- different URLs. It's one service, but its two different pieces of data. And the problem is - how do you ensure that if they quickly click twice, they don't see a result that may be confusing. ("I hit product 2 last, but I see product 1s info.") They aren't asking for the same token in my example, but rather 2 different ones.
It's only confusing if each response is not properly formulated, otherwise it doesn't really matter which response gets faster. Product 1 and product 2 have any relationship or dependence? If yes, the user should not be able to request but in a dependent manner.
Then I believe we will have to agree to disagree on this point.
Thanks Ray for the write up and your time. I have question say i have multiple async ajax request i make at the same time. how to handle such multiple request ?
I'd look into promises. If you go to my YouTube channel and view my jQuery tutorial series, I have one on just that topic.
I realize this is an old post but thought I'd chime in. This seems like a perfect case for async.series why go through all this trouble?
Can you talk about what this is and how it would act?
i'm stuck on this problem last 2 days....finally got it.........!!!!!!!!!!!!!!!!!!!!!
thanks a lot
I have written - when I was still very inexperienced in JS - an async queue manager that adds functions calls and ajax calls to a queue and runs them one-by-one and queue-after-queue to handle these cases with an added please wait animation. I don't do this that much lately as I found there is nothing wrong about getting data back in the wrong order and solved dependencies with deffered objects which are also good for animations by the way.
Anyway code is here without the fluff I have around it: http://jsfiddle.net/rawbits...
Not runnable in the fiddle though but perfectly working - at least I don't found any bugs.
I have a related question. My application loads bits of JS and JSON as required using AJAX. Occasionally though it would make a request to the same or same series of files. Ideally I don't want to do a server request for something that's in the memory already (files can be assumed to be static). What would be the best way you'd recommend I control for that?
One (important) thing to keep in mind is that the way my scripts are built is to wait for the files to load and then perform a series of tasks within onComplete{}. Is the only way to do this is to store variables that count whatever's been loaded? Or is there another way that would mean a lot less code restructuring for me?
I'd look into client-side storage. Modern browsers provide multiple ways to cache things in RAM/disk and let you fetch them later. I even wrote a book on it. :) (If you go to the About page here, you can see a link both to the book and to the video series I have on the topic.)
My Application , am calling a single ajax call when i call it loads the json file and when i select field in that it loads another json , here also one more link to json it also loading , here am increasing to pass one application to another application , ajax calls increasing and background also calling with each request , number of background pages increasing constantly
Ok - is that a question?
I ran into this same problem but came to a different solution than the ones mentioned so far. Though its similar to the send a token to and fro method mentioned by Ryan Guill below.
Extending object prototypes is a no no in JS. However extending instances is OK, as long you avoid name collisions.
By adding a myToken property to the XMLHttpRequest object instance you can see if the response handler is dealing with the request you want. If not, either ignore the result or when readyState < 4 abort() the request.
in code:
var LastEvent ; // this has to be global
function sendRequest(php, data) {
....var xhttp=new XMLHttpRequest();
........if (xhttp){
............xhttp.open("POST",php);
............xhttp.onreadystatechange=handleResponse;
............xhttp.myToken=getToken(); // get a unique identifier
............LastEvent=xhttp.myToken; // and remember the last one
............xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
............xhttp.send(data);
........}else alert("Get a better browser");
}
function handleResponse(e){
....if(LastEvent==this.myToken){ // Is this the response we want?
.........if (this.readyState==4){
.............if (this.status==200){
.................alert(this.responseText); // Do something with the result
.............}else alert(this.responseText); // Or show what went wrong
.........}else this.abort(); // Get rid of this request
....}
....e.stopPropagation();
}
Extremely helpful. Thanks!
Thanks a lot for sharing !! I
Thank you!
Hi Raymond, I'm trying to use your code, but I couldn't get it done. I'm trying to run a different query.php with the click of each button. I tried several ways, but couldn't make it. Something like this? <button data-prodid="query1.php" class="loadButton">Load One</button> but it didn't work. how can I do that using your code? please. thanks.
The data-prodid attribute is storing an ID value that gets read and made part of the request. But the request is to a hard coded ColdFusion file. You want to change that to your PHP and keep prodid as ID values.
yes, I wanted to run an sql query with the button click. I have several buttons, and actually have a working code for this, but it is very messy and long with repetitions.
Cool... so you got it? I can't tell by your comment if you got it working.
No, unfortunately, I couldn't use your code for this. I came across your page when I was looking for a simpler code to replace my existing code. At the moment, i use this code, and for every button, i repeat it. :
<script type="text/javascript">
function loadXMLDoc1()
{
var xmlhttp;
if (window.XMLHttpRequest)
{
xmlhttp=new XMLHttpRequest();
}
else
{
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("querydiv").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","query1.php",true);
xmlhttp.send();
}
</script>
Ok... but I explained how to fix the code though. You were using the URL in data-prodid, which in my demo represented a value you wanted to pass in to the back end service. If you literally change my jQuery code to point to your PHP and change the prodid values to be IDs, it should work. Of course, that assumes your back end code is a similar demo to mine.
Thanks, Raymond! I'll work on that. Have a great day!