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.
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.
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.
I have a function called:
function callAjax(url, data) {
$.ajax(
{
url: url, // same domain
data: data,
cache: false,
async: false, // use sync results
beforeSend: function() {
// show loading indicator
},
success: function() {
// remove loading indicator
}
}
);
}
In the code, I call "callAjax" X number of times and I want to update the data synchronously. It is done as expected, but one problem: the loading item doesn't show in beforeSend function. If I turn async to true, it works but the updates aren't synchronously done.
I've tried several things with no success. I tried putting the loading indicator before the ajax call like this:
function callAjax(url, data) {
// show loading div
$.ajax(
{
// same as above
}
);
}
But for some reason it doesn't want to show the loading indicator. I notice a strange behavior when I put an "alert" in the beforeSend and the loading indicator appears in that case, but I rather not pop up a message box.
Got any ideas?
Making a synchronous call like that is like putting up an "alert()" box. Some browsers stop what they're doing, completely, until the HTTP response is received.
Thus in your code, after your call to the "$.ajax()" function begins, nothing happens until the response is received, and the next thing as far as your code goes will be the "success" handler.
Generally, unless you're really confident in your server, it's a much better idea to use asynchronous calls. When you do it that way, the browser immediately returns to its work and simply listens in the background for the HTTP response. When the response arrives, your success handler will be invoked.
When you do the blocking I/O the program is halted until the the input is received, in JS words when doing a synchronous call, the program halts and browser window freezes (no painting can be done) until the response is received. In most cases doing syncronus calls and any kind of blocking I/O can be avoided. However imagine your doing a progress bar in java or any other programming language, you have to spawn a different thread to control the progress bar, I think.
One thing to try in your case, is to call the ajax call after a time delay
//loading div stuff,
//if your doing some animation here make sure to have Sufficient
//time for it. If its just a regular show then use a time delay of 100-200
setTimeout( ajaxCall, 500 );
EDIT ajaxcall in setTimeout, Example
This is what you are looking for - .ajaxStart()
It will be triggered when any ajax event starts
http://api.jquery.com/ajaxStart/
They even give a specific example similar to what you are trying to accomplish:
$("#loading").ajaxStart(function(){
$(this).show();
});
You can then use the .ajaxStop() function
$("#loading").ajaxStop(function(){
$(this).hide();
});
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.
So, I have a page that loads and through jquery.get makes several requests to populate drop downs with their values.
$(function() {
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
LoadContact();
};
It then calls LoadContact(); Which does another call, and when it returns it populates all the fields on the form. The problem is that often, the dropdowns aren't all populated, and thus, it can't set them to the correct value.
What I need to be able to do, is somehow have LoadContact only execute once the other methods are complete and callbacks done executing.
But, I don't want to have to put a bunch of flags in the end of the drop down population callbacks, that I then check, and have to have a recursive setTimeout call checking, prior to calling LoadContact();
Is there something in jQuery that allows me to say, "Execute this, when all of these are done."?
More Info
I am thinking something along these lines
$().executeAfter(
function () { // When these are done
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
},
LoadContact // Do this
);
...it would need to keep track of the ajax calls that happen during the execution of the methods, and when they are all complete, call LoadContact;
If I knew how to intercept ajax that are being made in that function, I could probably write a jQuery extension to do this.
My Solution
;(function($) {
$.fn.executeAfter = function(methods, callback) {
var stack = [];
var trackAjaxSend = function(event, XMLHttpRequest, ajaxOptions) {
var url = ajaxOptions.url;
stack.push(url);
}
var trackAjaxComplete = function(event, XMLHttpRequest, ajaxOptions) {
var url = ajaxOptions.url;
var index = jQuery.inArray(url, stack);
if (index >= 0) {
stack.splice(index, 1);
}
if (stack.length == 0) {
callback();
$this.unbind("ajaxComplete");
}
}
var $this = $(this);
$this.ajaxSend(trackAjaxSend)
$this.ajaxComplete(trackAjaxComplete)
methods();
$this.unbind("ajaxSend");
};
})(jQuery);
This binds to the ajaxSend event while the methods are being called and keeps a list of urls (need a better unique id though) that are called. It then unbinds from ajaxSend so only the requests we care about are tracked. It also binds to ajaxComplete and removes items from the stack as they return. When the stack reaches zero, it executes our callback, and unbinds the ajaxComplete event.
You can use .ajaxStop() like this:
$(function() {
$(document).ajaxStop(function() {
$(this).unbind("ajaxStop"); //prevent running again when other calls finish
LoadContact();
});
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
});
This will run when all current requests are finished then unbind itself so it doesn't run if future ajax calls in the page execute. Also, make sure to put it before your ajax calls, so it gets bound early enough, it's more important with .ajaxStart(), but best practice to do it with both.
Expanding on Tom Lianza's answer, $.when() is now a much better way to accomplish this than using .ajaxStop().
The only caveat is that you need to be sure the asynchronous methods you need to wait on return a Deferred object. Luckily jQuery ajax calls already do this by default. So to implement the scenario from the question, the methods that need to be waited on would look something like this:
function LoadCategories(argument){
var deferred = $.ajax({
// ajax setup
}).then(function(response){
// optional callback to handle this response
});
return deferred;
}
Then to call LoadContact() after all three ajax calls have returned and optionally executed their own individual callbacks:
// setting variables to emphasize that the functions must return deferred objects
var deferred1 = LoadCategories($('#Category'));
var deferred2 = LoadPositions($('#Position'));
var deferred3 = LoadDepartments($('#Department'));
$.when(deferred1, deferred2, deferred3).then(LoadContact);
If you're on Jquery 1.5 or later, I suspect the Deferred object is your best bet:
http://api.jquery.com/category/deferred-object/
The helper method, when, is also quite nice:
http://api.jquery.com/jQuery.when/
But, I don't want to have to put a bunch of flags in the end of the drop down population callbacks, that I then check, and have to have a recursive setTimeout call checking, prior to calling LoadContact();
No need for setTimeout. You just check in each callback that all three lists are populated (or better setup a counter, increase it in each callback and wait till it's equal to 3) and then call LoadContact from callback. Seems pretty easy to me.
ajaxStop approach might work to, I'm just not very familiar with it.