I have a page with multiple forms that I submit via Ajax POSTs serially. At first, I tried using synchronous XHR requests, but this causes the browser to lock up for the duration of the request, and breaks my DOM-manipulation effects, which is unacceptable. So the pattern I ended up using is basically this:
var fcount = 0; // incremented for each form to be submitted
function submit_form( num ) {
var fdata = { ... }; // data from form # num
$.ajax( { async: true,
url: '/index.cgi',
data: fdata,
type: 'POST',
success: function() {
if ( num < fcount ) {
submit_form( ++num );
}
}
} );
}
$( '#submit_form_btn' ).click( function() { submit_form( 1 ) } );
The recursion strikes me as a bit of an ugly solution to what is essentially an iterative problem. Is there a cleaner or more elegant way that this could be handled?
Are these requests idempotent? If they are you can simply fire them off one after the other. Otherwise you are somewhat limited by the asynchronous nature of AJAX.
UPDATE
I did some more research and apparently there exists a framework called jQuery Message Queuing that handles serial AJAX requests via message-queuing. Maybe this can help you out.
A cleaner way would be to maintain a queue of callbacks(ajax requests) that you want to make and fire them one by one.
I would say raise the timeout for your script on the server and send everything in one POST.
If what you want to do is what your code does it is just fine to use a recursion structure.
But the suspicious part here is using more than one call, you are wasting a lot of time submitting multiple calls. Stick all the data in one call and get it over with, if it is in any way possible that is by far the preferred method.
Related
I have written a simple web page where I would like to be able to execute concurrent ajax requests. I know that I can do concurrent ajax requests in jquery using .when() but that's not exactly my case. I have a function like the following:
function getData(tt, tf) {
$.ajax({
url : "/extpage.php",
type : "POST",
async: true,
data : {
testt : tt,
testf : tf
}
})
.done(function (toolbox) {
alert(data);
});
}
This function is called from a button inside the webpage and I need to be able to let the user call this function anytime he wants (I'm aware about the maximum number of the ajax requests that a browser can support) without waiting the previous ajax request to be finished first and then execute the next one. I want every call to be processed in parallel. Any clues on how I can obtain that ?
That's how AJAX works inherently. Each call you perform is run independent of any other browser activity (including, generally, other AJAX calls).
Given the function you have, if I call getData() ten times in a row, it will initiate ten independent HTTP requests. If they're not running concurrently it is possible that the server simply won't answer more than one request at a time, and of course you can't do anything about that.
I think you may need to revise your question.
This function is called from a button inside the webpage and I need to
be able to let the user call this function anytime he want
This is the default AJAX behaviour. AJAX calls are ansychronous.
async: true
is redundant, true is the default value for async.
Your code should do what you are asking in this question, if you are still experiencing a problem the issue may be elsewhere.
As one last note:
$.when()
is used to queue otherwise concurrent/async tasks, the opposite of what you suggested in the OP.
I am looking for an MVC implementation equivalent in functionality to the WebForms ajax request handlers below:
Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(function());
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(function());
A very similar question (link below) was answered with a reference to the global .ajax event handlers, but as all of my calls are synchronous I am having the issue that it is firing the ajax finished event at the end of each individual ajax request, and I want it to start with the first and end when the final one finishes. I tried using ajaxStart and ajaxStop which the documentation suggests should be exactly what I am looking for, but I belive that these would only work as intended with async ajax requests.
(ASP.NET MVC 4 Action on AJAX Request)
Is there any way to do this using the jQuery built in ajax event handlers, or any alternative method for synchronous requests?
You can use jQuery's .when (in combination with .apply) which allows you to execute a callback function based on any number of Deferred objects (which are returned by jQuery .ajax calls). Check out the fiddle and open up dev tools, if you watch the log you will see the correct order of events. First 'started' is logged, then a number of 'ajax response' messages are logged, and finally the 'complete' message is logged at the end of all requests.
Fiddle: http://jsfiddle.net/efjmcm49/
var ajaxCalls = [];
console.log('started');
for(var i = 1; i <= 5; i++){
var currentCall = $.ajax({
url: '/echo/json',
type: 'GET',
success:function(data){
console.log('ajax response');
}
});
ajaxCalls.push(currentCall);
}
$.when.apply($, ajaxCalls).done(function () {
console.log('complete');
});
In a series of synchronous requests, jQuery cannot know when you are done making all of them. How would it know that you aren't just about to start another one?
If the requests are synchronous can't you just use normal program flow? Like:
ajaxStart(); //execute arbitrary code
while(condition) {
//ajax requests here
}
ajaxStop(); //execute arbitrary code
jquery don't have anything inbuilt like that but you can do one thing, take one counter and
initialize with 0 and in each ajaxstart you can increment by 1 and in success/error of ajax
decrement counter by 1 and as soon as you get value of counter as 0 means all request is over.
In Javascript, I have two asychronous requests for data:
$.getJSON('http://foo.com', fooQuery, fooSuccess(data));
$.getJSON('http://bar.com', barQuery, barSuccess(data));
and two callbacks to process the received data:
fooSuccess(data) { // Stuff }
barSuccess(data) { // More Stuff }
How do I ensure barSuccess is executed only after fooSuccess completes?
Notes:
I want to keep the data requests as they are: asynchronous and non-blocking (since server responses may take a while).
But, I want the callbacks that process the data to be executed sequentially. That is, I do not want to execute barSuccess until fooSuccess completes.
Thanks so much for your wisdom and help!
Here is how you would do it using the jQuery deferred object that is returned by ajax requests.
var fooDfd = $.getJSON('http://foo.com', fooQuery);
var barDfd = $.getJSON('http://bar.com', barQuery);
fooDfd.then(function(fooData){
fooSuccess(fooData);
barDfd.then(barSuccess);
});
The best way would be to utilize the jQuery when().done() functionality like this:
$.when(
$.getJSON('http://foo.com', fooQuery, fooSuccess(data)),
$.getJSON('http://bar.com', barQuery, barSuccess(data))
).done(function(arg1, arg2){
fooSuccess(arg1);
barSuccess(arg2);
});
This allow simultaneous execution of the AJAX requests and guaranteed execution of the done() function once all requests has successfully completed.
I'm follow this very interesting post from a half an hour ago, when appear the elegant solution presented by #Mike Brant I quickly was to dive in the jquery library to see how the magic is made. Don't you? I recommend, is very interesting!
BTW I think we don't need all that magic, not in this case, we have two asynchronous calls handlers(functions), no matter which end first, we need to know when the second end, then all we need is a third function that will be called by the two handlers and act when all the data is ready. I know this approach will vaste four or five lines more of code than the elegant jquery solution, but at the end our brain and soul will be in better condition. Sorry my english.
Put the barSuccess call in the fooSuccess success callback.
fooSuccess(data){
jQuery.ajax({
data: data,
success: function(response){
barSuccess(data) //should still be in scope, I think?
}
}
}
Is there an event in javascript that I could bind some sort of listener to that will tell me when all javascript/jQuery/Ajax is done executing on the page? The page will not be loading/unloading/reloading, etc between the time the execution begins and the time that I need the listener to "listen", so those events don't work. The page literally is not doing anything. The button is clicked and some javascript functions fire which contain Ajax calls to web services. After all have finished, I want to change window.location. But window.location is changing before the web services have finished in my case.
Currently using setTimeout to achieve this, but as sometimes the code needs more time to run than normal, sometimes the window.location is firing before all the other javascript has finished. Simply put
<input type = "button"... onclick="doThis();";
function doThis() {
try{
//Contains AJAX calls to web services which is mainly what screws up my timing since it may still be trying to execute stuff when the redirect statement happens
}
catch (e) {
}
//Currently doing setTimeout(redirect, 10000);
//Would like to simply detect when all of the above is done and then redirect.
}
Edit: Left out a crucial piece of info. The AJAX calls are in a for loop. The use of variables and success callbacks hasn't been working so well for me as by the time my success callback is executing, my variables have taken on new values in the for loop.
What you are trying to achieve is a classical concurrent programming problem. It is solved by the use of a barrier.
To put it simply, you need to:
Count how many calls you've done.
Set a callback on all AJAX completion events.
Make that callback decrement the number of calls.
The callback checks whether the number of calls has reached zero or not. If yes, then your final code (here, redirect) is called.
The actual implementation is left as an exercise to the reader :)
Hint: embed AJAX calls into a function that handles all counter incrementation and callback setting.
What I do:
Create a variable that represents the number of outstanding AJAX calls.
Before making an AJAX call, increment the variable.
At the end of the code that completes an AJAX call, call a function (e.g. ajaxComplete).
ajaxComplete should decrement the count. When it reaches zero, you know all your calls are complete.
Assuming you're using jQuery.ajax, it sounds like you're looking for ajaxStop.
Why don't you try using something like the Underscore library's after function in the callbacks?
var done = _.after(3, function() {
window.location = 'http://example.com';
});
$.ajax({
url: '/tic',
success: function() {
done();
}
});
$.ajax({
url: '/tac',
success: function() {
done();
}
});
$.ajax({
url: '/toe',
success: function( data ) {
done();
}
});
You should check for the response from AJAX call, and only in that response do redirect. This way you will avoid doing redirect while AJAX was still executing.
This is a really basic JavaScript question and probably duplicate, but I don't know the answer!
I have code as follows:
function userlist_change(myval, function_type) {
// relatively slow code involving Ajax call
// based on Ajax results, change some client-side stuff
}
$("#subjectlist").change(function() {
userlist_change($("#subjectlist").val(), 'change');
}).change();
$("#subjectlist").keypress(function() {
userlist_change($("#subjectlist").val(), 'keypress');
});
I have the problem that if the .change() event is called, the userlist_change function kicks off, and it's relatively slow. If the user changes the list again (e.g. by typing), my code waits for userlist_change to complete before restarting it with the new value.
This looks quite odd in the UI, as it can take a few seconds for anything to change client-side - and sometimes the results of the first call only appear after the user has already made a second call.
Is there any way I can interrupt any existing userlist_change process when the .change() or `keypress() event is fired?
[EDIT] What would be ideal is a simple 'kill any running functions with this name' command - is this possible? Or do I really have to fiddle around with timers?!
you can store last request time in a global variable, and store a request time in each ajax request, so that when you are just showing the result of first request, if the global last request time is greater than request, request time, you should show, other wise not. For example:
var lastRequestTime;
function userlist_change(myval, function_type,requestTime) {
// relatively slow code involving Ajax call
// based on Ajax results, change some client-side stuff
if(lastRequestTime <= requestTime){
//show
}
}
$("#subjectlist").change(function() {
lastRequestTime = new Date();
userlist_change($("#subjectlist").val(), 'change',lastRequestTime );
}).change();
$("#subjectlist").keypress(function() {
lastRequestTime = new Date();
userlist_change($("#subjectlist").val(), 'keypress',lastRequestTime );
});
You should use throttling of event. It is quite easily done with RX for JavaScript, but library is quite complicated. You can try filter value with timer.
Here is useful plugin for throttling: http://benalman.com/projects/jquery-throttle-debounce-plugin/