Cancelling AJAX request if previous AJAX response hasn't been received [duplicate] - javascript

Is it possible that using jQuery, I cancel/abort an Ajax request that I have not yet received the response from?

Most of the jQuery Ajax methods return an XMLHttpRequest (or the equivalent) object, so you can just use abort().
See the documentation:
abort Method (MSDN). Cancels the current HTTP request.
abort() (MDN). If the request has been sent already, this method will abort the request.
var xhr = $.ajax({
type: "POST",
url: "some.php",
data: "name=John&location=Boston",
success: function(msg){
alert( "Data Saved: " + msg );
}
});
//kill the request
xhr.abort()
UPDATE:
As of jQuery 1.5 the returned object is a wrapper for the native XMLHttpRequest object called jqXHR. This object appears to expose all of the native properties and methods so the above example still works. See The jqXHR Object (jQuery API documentation).
UPDATE 2:
As of jQuery 3, the ajax method now returns a promise with extra methods (like abort), so the above code still works, though the object being returned is not an xhr any more. See the 3.0 blog here.
UPDATE 3: xhr.abort() still works on jQuery 3.x. Don't assume the update 2 is correct. More info on jQuery Github repository.

You can't recall the request but you can set a timeout value after which the response will be ignored. See this page for jquery AJAX options. I believe that your error callback will be called if the timeout period is exceeded. There is already a default timeout on every AJAX request.
You can also use the abort() method on the request object but, while it will cause the client to stop listening for the event, it may probably will not stop the server from processing it.

Save the calls you make in an array, then call xhr.abort() on each.
HUGE CAVEAT: You can abort a request, but that's only the client side. The server side could still be processing the request. If you are using something like PHP or ASP with session data, the session data is locked until the ajax has finished. So, to allow the user to continue browsing the website, you have to call session_write_close(). This saves the session and unlocks it so that other pages waiting to continue will proceed. Without this, several pages can be waiting for the lock to be removed.

It's an asynchronous request, meaning once it's sent it's out there.
In case your server is starting a very expensive operation due to the AJAX request, the best you can do is open your server to listen for cancel requests, and send a separate AJAX request notifying the server to stop whatever it's doing.
Otherwise, simply ignore the AJAX response.

AJAX requests may not complete in the order they were started. Instead of aborting, you can choose to ignore all AJAX responses except for the most recent one:
Create a counter
Increment the counter when you initiate AJAX request
Use the current value of counter to "stamp" the request
In the success callback compare the stamp with the counter to check if it was the most recent request
Rough outline of code:
var xhrCount = 0;
function sendXHR() {
// sequence number for the current invocation of function
var seqNumber = ++xhrCount;
$.post("/echo/json/", { delay: Math.floor(Math.random() * 5) }, function() {
// this works because of the way closures work
if (seqNumber === xhrCount) {
console.log("Process the response");
} else {
console.log("Ignore the response");
}
});
}
sendXHR();
sendXHR();
sendXHR();
// AJAX requests complete in any order but only the last
// one will trigger "Process the response" message
Demo on jsFiddle

We just had to work around this problem and tested three different approaches.
does cancel the request as suggested by #meouw
execute all request but only processes the result of the last submit
prevents new requests as long as another one is still pending
var Ajax1 = {
call: function() {
if (typeof this.xhr !== 'undefined')
this.xhr.abort();
this.xhr = $.ajax({
url: 'your/long/running/request/path',
type: 'GET',
success: function(data) {
//process response
}
});
}
};
var Ajax2 = {
counter: 0,
call: function() {
var self = this,
seq = ++this.counter;
$.ajax({
url: 'your/long/running/request/path',
type: 'GET',
success: function(data) {
if (seq === self.counter) {
//process response
}
}
});
}
};
var Ajax3 = {
active: false,
call: function() {
if (this.active === false) {
this.active = true;
var self = this;
$.ajax({
url: 'your/long/running/request/path',
type: 'GET',
success: function(data) {
//process response
},
complete: function() {
self.active = false;
}
});
}
}
};
$(function() {
$('#button').click(function(e) {
Ajax3.call();
});
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="button" type="button" value="click" />
In our case we decided to use approach #3 as it produces less load for the server. But I am not 100% sure if jQuery guarantees the call of the .complete()-method, this could produce a deadlock situation. In our tests we could not reproduce such a situation.

It is always best practice to do something like this.
var $request;
if ($request != null){
$request.abort();
$request = null;
}
$request = $.ajax({
type : "POST", //TODO: Must be changed to POST
url : "yourfile.php",
data : "data"
}).done(function(msg) {
alert(msg);
});
But it is much better if you check an if statement to check whether the ajax request is null or not.

Just call xhr.abort() whether it's jquery ajax object or native XMLHTTPRequest object.
example:
//jQuery ajax
$(document).ready(function(){
var xhr = $.get('/server');
setTimeout(function(){xhr.abort();}, 2000);
});
//native XMLHTTPRequest
var xhr = new XMLHttpRequest();
xhr.open('GET','/server',true);
xhr.send();
setTimeout(function(){xhr.abort();}, 2000);

You can abort any continuous ajax call by using this
<input id="searchbox" name="searchbox" type="text" />
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script type="text/javascript">
var request = null;
$('#searchbox').keyup(function () {
var id = $(this).val();
request = $.ajax({
type: "POST", //TODO: Must be changed to POST
url: "index.php",
data: {'id':id},
success: function () {
},
beforeSend: function () {
if (request !== null) {
request.abort();
}
}
});
});
</script>

As many people on the thread have noted, just because the request is aborted on the client-side, the server will still process the request. This creates unnecessary load on the server because it's doing work that we've quit listening to on the front-end.
The problem I was trying to solve (that others may run in to as well) is that when the user entered information in an input field, I wanted to fire off a request for a Google Instant type of feel.
To avoid firing unnecessary requests and to maintain the snappiness of the front-end, I did the following:
var xhrQueue = [];
var xhrCount = 0;
$('#search_q').keyup(function(){
xhrQueue.push(xhrCount);
setTimeout(function(){
xhrCount = ++xhrCount;
if (xhrCount === xhrQueue.length) {
// Fire Your XHR //
}
}, 150);
});
This will essentially send one request every 150ms (a variable that you can customize for your own needs). If you're having trouble understanding what exactly is happening here, log xhrCount and xhrQueue to the console just before the if block.

I was doing a live search solution and needed to cancel pending requests that may have taken longer than the latest/most current request.
In my case I used something like this:
//On document ready
var ajax_inprocess = false;
$(document).ajaxStart(function() {
ajax_inprocess = true;
});
$(document).ajaxStop(function() {
ajax_inprocess = false;
});
//Snippet from live search function
if (ajax_inprocess == true)
{
request.abort();
}
//Call for new request

Just use ajax.abort() for example you could abort any pending ajax request before sending another one like this
//check for existing ajax request
if(ajax){
ajax.abort();
}
//then you make another ajax request
$.ajax(
//your code here
);

there is no reliable way to do it, and I would not even try it, once the request is on the go; the only way to react reasonably is to ignore the response.
in most cases, it may happen in situations like: a user clicks too often on a button triggering many consecutive XHR, here you have many options, either block the button till XHR is returned, or dont even trigger new XHR while another is running hinting the user to lean back - or discard any pending XHR response but the recent.

The following code shows initiating as well as aborting an Ajax request:
function libAjax(){
var req;
function start(){
req = $.ajax({
url: '1.php',
success: function(data){
console.log(data)
}
});
}
function stop(){
req.abort();
}
return {start:start,stop:stop}
}
var obj = libAjax();
$(".go").click(function(){
obj.start();
})
$(".stop").click(function(){
obj.stop();
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="button" class="go" value="GO!" >
<input type="button" class="stop" value="STOP!" >

If xhr.abort(); causes page reload,
Then you can set onreadystatechange before abort to prevent:
// ↓ prevent page reload by abort()
xhr.onreadystatechange = null;
// ↓ may cause page reload
xhr.abort();

I had the problem of polling and once the page was closed the poll continued so in my cause a user would miss an update as a mysql value was being set for the next 50 seconds after page closing, even though I killed the ajax request, I figured away around, using $_SESSION to set a var won't update in the poll its self until its ended and a new one has started, so what I did was set a value in my database as 0 = offpage , while I'm polling I query that row and return false; when it's 0 as querying in polling will get you current values obviously...
I hope this helped

I have shared a demo that demonstrates how to cancel an AJAX request-- if data is not returned from the server within a predefined wait time.
HTML :
<div id="info"></div>
JS CODE:
var isDataReceived= false, waitTime= 1000;
$(function() {
// Ajax request sent.
var xhr= $.ajax({
url: 'http://api.joind.in/v2.1/talks/10889',
data: {
format: 'json'
},
dataType: 'jsonp',
success: function(data) {
isDataReceived= true;
$('#info').text(data.talks[0].talk_title);
},
type: 'GET'
});
// Cancel ajax request if data is not loaded within 1sec.
setTimeout(function(){
if(!isDataReceived)
xhr.abort();
},waitTime);
});

This is my implementation based on many answers above:
var activeRequest = false; //global var
var filters = {...};
apply_filters(filters);
//function triggering the ajax request
function apply_filters(filters){
//prepare data and other functionalities
var data = {};
//limit the ajax calls
if (activeRequest === false){
activeRequest = true;
}else{
//abort if another ajax call is pending
$request.abort();
//just to be sure the ajax didn't complete before and activeRequest it's already false
activeRequest = true;
}
$request = $.ajax({
url : window.location.origin + '/your-url.php',
data: data,
type:'POST',
beforeSend: function(){
$('#ajax-loader-custom').show();
$('#blur-on-loading').addClass('blur');
},
success:function(data_filters){
data_filters = $.parseJSON(data_filters);
if( data_filters.posts ) {
$(document).find('#multiple-products ul.products li:last-child').after(data_filters.posts).fadeIn();
}
else{
return;
}
$('#ajax-loader-custom').fadeOut();
},
complete: function() {
activeRequest = false;
}
});
}

Related

JavaScript and Scope Issue

I have an issue with calling ajax request with jquery.
The order that I'm doing this in is:
click the button
do ajax post
when the ajax request is finished I call a function that is out side the scope.
For some reason and supecting that it has to do with the fact that i am in the on click callback that the load function is out of scope. But I don't even see the console.log message either. But I do see the ajax call.
Any ideas? Maybe I'm doing this the wrong way???
Here's the prototype code that resembles what I'm trying to do:
$(document).ready(function(){
$('#button').on('click',function(evt){
var data = {};
ajax('index.html', data).done(function(){
console.log('Fire Please'); // this does not fire after the ajax call!!!
load(); // this does not fire after the ajax call!!!
});
});
function load(){
// do another ajax call and add to the dom
}
function ajax(url, data){
return $.ajax({
url: url,
type: 'post',
dataType: 'json',
data: data
});
}
});
And Here's the Actual Code I'm trying to use
$(document).ready(function(){
// add onclick event to the Add Unit Button
addUnitButt.on('click', function(evt){
var data = {
id: id,
dept_no: dept_no.val(),
dept: dept.val()
};
evt.preventDefault();
dept.val('');
dept_no.val('');
$(this).prop('disabled', true);
ajax('index.html', data).done(function(){
load();
});
});
function load(){
var data = {
id: 575
};
// show loading
showLoading();
// reset the table dom
$("#listTable").find("tr:gt(0)").remove();
// do initial load of the list data
ajax('index.html', data)
.done(function(units){
var data = toJSONObject(units);
for(var x = 0; x < data.length; x++){
if((x & 1) == 0){
addRow(data[x], data.length, 'odd');
}else{
addRow(data[x], data.length, 'even');
}
}
// hide loading
hideLoading();
});
}
// ajax function to call for data
function ajax(url, data){
return $.ajax({
type: 'POST',
data: data,
dataType: 'json',
url: url
});
}
});
Thanks in advance!
Maybe the code of your index.html is not valid JSON
.always() must be called.
Make sure your server response have the right headers like Content-Type. And the response body is valid JSON.

Mutually exclusive submits

I have a javascript which on a "submit" event does the following ajax call(which in turn triggers a python script),my problem now is that "when one submit event is going on if anyone else clicks on
the submit button this ajax call should notify that a submission is in progress" ,has anyone ran into this problem?(is there a name?) ,how do fix this problem?
Please suggest..
$("#main_form").submit(function(event) {
.....................
$.ajax({
dataType: "json",
type: "POST",
contentType: "application/json",//note the contentType definition
url: "scripts/cherrypick.py",
data: JSON.stringify(data_cp),
//data: data_cp,
error : function (xhr, ajaxOptions, thrownError){
console.log("cherypick fail");
console.log(response);
console.log(response['returnArray']);
alert(xhr.status);
alert(thrownError);
},
success: function(response){
console.log("cherypick sucess");
console.log(response);
console.log(response['returnArray']);
var return_array = response['returnArray'];
console.log(return_array['faillist'].length);
console.log(return_array['picklist'].length);
for (var i = 0; i < ip_gerrits.length; ) {
for (var j = 0; j < return_array['faillist'].length; ) {
if (ip_gerrits[i] != return_array['faillist'][j] )
ipgerrits_pickuplist.push(ip_gerrits[i]);
j++;
}
i++;
}
Ok, as far as you want to synchronize requests processing for all users, it should be done on the server side. I assume that your server side is Python, even though you did not add relevant tag to your question. My preferences are C# and PHP, but in your case I would do the following ...
Options # 1 - Session
1) add or install preferable session module for Python, crowd recommends to use Beaker
Python Module for Session Management
2) send AJAX request to the server side script
$(form).submit(function(e) {
var options = {
url: "scripts/cherrypick.py"
};
$.ajax(options);
});
3) this server side script will have something like this code
session_opts = {
'session.type': 'file',
'session.data_dir': './session/',
'session.auto': True,
}
app = beaker.middleware.SessionMiddleware(bottle.app(), session_opts)
#hook('before_request')
def setup_request():
request.session = request.environ['beaker.session']
#route('/cherrypick')
def index():
if 'processing' in request.session:
data = { 'procesing': request.session['processing'] }
return data
processor()
def processor():
request.session['processing'] = 1
# Do some processing here for the first request
# When processing is done you can clear "state" variable in session
del request.session['processing']
request.session.modified = True
Bottle.py session with Beaker
http://beaker.readthedocs.org/en/latest/sessions.html#using
http://flask.pocoo.org/snippets/61/
4) Now in your JS script if you get JSON that contains key "processing" you may show alert to the user that he needs to wait until first request is processed
Option # 2 - Long Polling and Comet
Description of this option may take much more space to describe, thus it is better to look at this article, it has quite nice and clean example and implementation of long polling in Python
http://blog.oddbit.com/2013/11/23/long-polling-with-ja/
The main idea here is not to keep static session but use infinite loop instead that can send back different HTTP responses depending on some state variable :
#route('/cherrypick')
def index():
while True :
response = { 'processing': processing }
print response
if processing != 1 :
processing = 1
# Do some processing
processing = 0
sleep(5)
The simplest way is to close around a flag that indicates some processing is underway:
var processing = false;
$("#main_form").submit(function(event) {
if (processing) {
$("#some_notification_pane").text("hold on there, turbo!");
return;
}
processing = true;
...
$.ajax({
...
error: function(xhr, ajaxOptions, thrownError) {
...
processing = false;
},
success: function(response) {
...
processing = false;
}
});
...
});
You might also want to disable the submit button at the beginning of the submit handler (where I have processing = true) and re-enable it after receiving a response.

Calling jQuery Get after Post

I am trying to call a jQuery GET request on the successful completion of a POST request. The functions work and the data is being fed through from the GET request, however, it is responding before the POST.
function getCartCount() {
var d = new Date().getTime();
$.get("/ajax/countCart.php", { "rand": d }, function(res) {
$("#view-cart").text(res);
alert(res);
});
}
$(".products form img").click(function() {
$.post("/ajax/addToCart.php", $(this).parent("form").serialize())
.done(function(data) {
alert(data);
})
.always(getCartCount());
});
The above code produces an alert box from the GET request first then an alert box from the POST which is not ideal as the value from the GET is dependent on the POST being completed first.
Please see http://www.cccomforts.co.uk/small-furries for the output.
.always(getCartCount());
^^
You are calling the function immediately and passing the return value to always.
Remove the () to pass the function itself.
.always(getCartCount);
This is because you are not waiting for POST request to complete successfully. Do this -
$(".products form img").click(function() {
$.post("/ajax/addToCart.php", $(this).parent("form").serialize())
.done(function(data) {
alert(data);
getCartCount();
})
});

How can I execute a query on success of jQuery

I am using jQuery to delete some data from database. I want some functionality that when jQuery returns success I want to execute a query. I want to update a another table on success of jQuery without page refresh. Can I do this and if yes how can I do this?
I am newbie to jQuery so please don't mind if it's not a good question for stackoverflow.
This is my script:
<script type="text/javascript">
$(document).ready(function () {
function delete_comment(autoid, btn_primary_ref) {
$.ajax({
url: 'rootbase.php?do=task_manager&element=delete_comment',
type: "POST",
dataType: 'html',
data: {
autoid: autoid
},
success: function (data) {
// I want to execute the Update Query Here
alert("Comment Deleted Successfully");
$(btn_primary_ref).parent().parent().hide();
var first_visible_comment = $(btn_primary_ref).parent().parent().parent().children().find('div:visible:first').eq(0).children('label').text();
if (first_visible_comment == "") {} else {
$(btn_primary_ref).parent().parent().parent().parent().parent().parent().prev().children().text(first_visible_comment);
}
load_comment_function_submit_button(autoid, btn_primary_ref);
},
});
}
$(document).on('click', '.delete_user_comment', function (event) {
var autoid = $(this).attr('id');
var btn_primary_ref = $(this);
var r = confirm("Are you sure to delete a comment");
if (r == true) {
delete_comment(autoid, btn_primary_ref);
} else {
return false;
}
});
});
</script>
You can't do database operations directly in Javascript. What you need to do is to simply make a new AJAX request on success to a php file on the backend to update given table. However this would mean two AJAX requests to the backend, both of which manages database data. Seems a bit unnecessary. Why not just do the update operation after the delete operation in the php file itself?
add a server sided coded page that will execute your query.
example :
lets say you add a page named executequery.php.
with this code:
when you want to execute your query do the following :
$.post("executequery.php",//the URL of the page
{
param1:value1,
param2:value2....//if you want to pass some parameters to the page if not set it to null or {}
},
function(data){
//this is the callback that get executed after the page finished executing the code in it
//the "data" variable contain what the page returened
}
);
PS : tha paramters sent to the page are conidired like $_POST variables in the php page
there is an other solution but its UNSAFE i recomand to NOT use it.
its to send the query with the paramters and that way you can execute the any query with the same page example :
$.post("executequery.php",//the URL of the page
{
query:"insert into table values("
param1:value1,
param2:value2....//if you want to pass some parameters to the page if not set it to null or {}
},
function(data){});

call another ajax function recursively from ajax success

I have a web page that is used to request the reports. When the user clicks a button, I am calling a function that will make a ajax request to process the reports. Once the request is made, I am using setInterval function to check the status of the report (whether is completed or not) every 1 second. Some how, the function inside setInterval() is not called at all.
Following is the javascript code that I have
var GLFiles_JSObject = {
url: "<%= Request.ApplicationPath %>/AjaxRequest.aspx",
apppath: '<%= Request.ApplicationPath %>',
btnRefreshId: '<%= btnRefresh.ClientID %>',
ajaxFunctionName: 'GLBuilder_ReprocessFiles',
reportstatusFuncName: 'GLBuilder_GetReportStatus',
reportid: 0,
ajaxError: function(XMLHttpRequest, textStatus, errorThrown){
alert(XMLHttpRequest.statusText);
},
ajaxSuccess: function(msg) {
if (msg === '') {
setInterval(function(){
this.reportstatus();
}, 1000);
}
},
reportstatusSuccess : function(msg) {
if (msg === '1') {
clearInterval();
}
},
reportstatus : function() {
var keys = new Array('reportid');
var values = new Array(reportid);
//ajax call
WebServicePost(true, this.url, this.reportstatusFuncName, keys, values, this.ajaxError, this.reportstatusSuccess);
}
};
//this will be called when button is clicked.
function reprocessGLFiles(reportid, btnid) {
//disable the button
//$('#' + btnid).attr("disabled", true);
GLFiles_JSObject.reportid = reportid;
var keys = new Array('reportid');
var values = new Array(GLFiles_JSObject.reportid);
// make an ajax request to process the files
WebServicePost(true, GLFiles_JSObject.url, GLFiles_JSObject.ajaxFunctionName, keys, values, GLFiles_JSObject.ajaxError, GLFiles_JSObject.ajaxSuccess);
return false;
}
The reason it is not being called is probably because you using the this var which means something else. If you change this.reportstatus(); to GLFiles_JSObject.reportstatus() will probably fix your problem.
On a different note, I think you are misunderstanding something here. Why do you need to call a timer method for this to check status. OnSuccess is called when the method the ajax request is finished. So you don't need to call setTimeout.

Categories