Hello I have multiple ajax call, what I wanted to do is to fire all of them asynchronously then wait till all of them are finished before processing the returned data. I tried using $.when but to no avail. Here is my code:
//form_identifier_list is my flag to get the multiple forms in my html page
function test(form_identifier_list){
var deffereds = [];
$.each(form_identifier_list, function(key,value){
var this_form = $(this).parents('.ajaxed_form');
deffereds.push( $.post($(this_form).attr("action"), $(this_form).serializeForm()) );
});
$.when.apply($, deffereds).done(function(){
//how to output response obj?? i tried console.log(data) to no avail
}).fail(function(){
}).always(function(){
});
}
I also noticed my ajax requests do not have reponse (which i verified on my browser).
Is there a way to make multiple ajax calls fire asynchronously and then wait till all of them are finished then access the data?
Thanks
In the jquery page on when, there is a section that says:
In the case where multiple Deferred objects are passed to
jQuery.when(), the method returns the Promise from a new "master"
Deferred object that tracks the aggregate state of all the Deferreds
it has been passed.
Also there is this on the ajax page
The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the
Promise interface
So we can just assign the return value of the ajax to a variable and pass those variables into the .when.
To expand slightly on the example given on the .when page and integrate any number of ajax calls, you can do something like this:
var ajaxes = [];
for(var i=0; i<10; i++) {
ajaxes.push($.ajax('a.php', {data: {t: i}}));
}
$.when.apply($, ajaxes)
.done(function(){
for(var i=0;i<arguments.length; i++) {
$('#output').append(arguments[i] + "<br>");
};
});
If you have a known number of ajax calls, it gets a little simpler...you can do something more like this:
var a1 = $.ajax(...);
var a2 = $.ajax(...);
var a3 = $.ajax(...);
$.when(a1,a2,a3).done(function(o1, o2, o3) {
$('#output').append(o1).append(o2).append(o3);
});
The first example is basically doing the same thing.
It is possible to asynchronously call on several JavaScript functions (e.g. Ajax functions), wait for each of the functions to finish and then continue (e.g. access data) using setInterval.
Consider three JavaScript functions (i=0,1,2). Each function performs the same number of operations (10). After the system starts (T_0), each of the functions has a beginning (T_(3i+2) or〖 T〗_2,〖 T〗_5 and〖 T〗8), an operation to perform ten times (T(3i+3) or〖 T〗_3, 〖 T〗_6 and〖 T〗9), and an ending (T(3i+4) or〖 T〗_4, 〖 T〗_7 and〖 T〗_10). When the functions are ready to begin, the functions may start in any order. Each function performs its operations ten times. When every function has finished performing its operation, the system continues (T_1) and the three functions may be performed again.
Figure 1: A Petri Net Model for Synchronizing Three Function Calls
The function that setInterval executes does the following:
Selects an (ajax) function to execute, and executes the function. For example each of T_3, T_6 and〖 T〗_9 in Figure 1 may represent a function call for an (ajax) function that is called ten times.
If every (ajax) function has finished its task then the function continues and clears the interval object.
Consider the following sample code snippet:
var s1=false, s2=false, s3=false;
// a1 (an Ajax function) sets s1 to true if it is finished
function a1() { …}
// a2 (an Ajax function) sets s2 to true if it is finished
function a2() {…}
// a3 (an Ajax function) sets s3 to true if it is finished
function a3() {…}
// fcon is the function to execute when a1, a2 and a3 has finished.
function fcon() {…}
function myFunction() {
var af = [], i;
If (s1) af[af.length] = 1;
If (s2) af[af.length] = 2;
If (s3) af[af.length] = 3;
If (af.length==0) {
fcon();
clearInterval(ai);
ai = undefined;
} else {
i = Math.round( (af.length-1)*Math.random());
switch(af[i]) {
case 1:
a1(); break;
case 2:
a2(); break;
case 3:
a3(); break;
default:
break;
}
}
}
var ai = setInterval(“myFunction()”, 1000);
[This answer has a PDF version whose figure is dynamic and interactive.]
I want to suggest you something dirty (:
var counter=0;
$.ajax(success:function(){counter++})
$.ajax(success:function(){counter++})
$.ajax(success:function(){counter++})
$.ajax(success:function(){counter++})
$.ajax(success:function(){counter++})
var interval = setInterval(function(){
If(counter===5){
// fire event to elaborate data
// clear interval
}
})
Related
In my project, data is distributed across groups of table. For reading the data, I need to make Async call to each of these groups (1...groupCount).
I need to call another function after all the data present in each of these groups is successfully read. What is the best way to do so?
function getData() {
for(var gc = 1; gc < groupCount; gc++)
readDataFromAsync(gc);
}
Assuming readDataFromAsync returns a jQuery deferred object
Use jQuery.when() and pass a callback to run when all is done.
function getData() {
var promises = [];
for (var gc = 1; gc < groupCount; gc++) {
promises.push(readDataFromAsync(gc));
}
$.when.apply(undefined, promises).done(function(/*...*/) {
// your code
});
}
First of all it is a bad practice to call AJAX several time, like inside of a loop.
If you can combine all the call and send all the request at a time, and take back all the response at a time then it will be best approach. For that you need to update server side code also.
But it is not possible always and depending on circumstances you need to call like this ways.
Here is some alternatives.
Use synchronous call insteted of async as follows
jQuery.ajax({
url: '.....',
success: function (result) {
//.....
},
async: false
});
But it will be slow since 1 by 1 call is there and not at all a good practice.
So you could count the ajaxResponse as a variable on your successful or unsuccessful response and call a final method when ajaxResponse reach it's cap as follows.
var ajaxResponse = 0;
function getData() {
ajaxResponse= 0;
for(var gc = 1; gc < groupCount; gc++)
readDataFromAsync(gc);
}
function readDataFromAsync(gc) {
$.ajax(...).success() {
//Your current code
ajaxResponse++;
if(ajaxResponse>=groupCount){
finalCall();
}
}
}
But there is problem also for the above approach. Handling ajaxResponse counter is difficult and there could be error occurred on ajax call.
You could also try setInterval and clearInterval instated of putting one if inside the success method but it will also costly.
Last one but best is approach provided by #Li Yin Kong as follows.
function getData() {
var promises = [];
for (var gc = 1; gc < groupCount; gc++) {
promises.push(readDataFromAsync(gc));
}
$.when.apply(undefined, promises).done(function(/*...*/) {
// your code
});
}
I have a an array called "products" that varies on each page load. I then need to call a function "addProduct()" for as many times as "products" is long, and use an altered value of each index inside that function. The function is long a complex and uses asynchronous calls (like .get) How can I stop the for loop from going to the next iteration until addProduct() is finished executing?
function addProduct(productVariable){
// (...do some long and asynchronous things with productVariable...)
}
//products array can vary in length on each page load
products = ["prod01","prod02","prod03","prod04","prod05",...];
for (var i = 0; i < products.length; i++) {
var productAlter = product[i];
//(...do some things with productAlter, but wait
// until the previous loop's addProduct() function
// has completed successfully)
addProduct(productAlter);
}
I keep reading about callbacks and promises, but no matter when I put them, the function seems to fire asynchronously. Any help is appreciated!
Will make use of success callback for Ajax to call addNextProdut and if the index reach its max then it will stop.
function addProduct(productVariable){
$.ajax({
url: your_action_server_url,
context: document.body,
success: function(){
addNextProduct()
}
})
}
function addNextProduct(){
index++;
if(index < products.length)
addProduct(products[index])
}
products = ["prod01","prod02","prod03","prod04","prod05",...];
index = 0;
addProduct(products[index])
I have a JavaScript constructor function that I want to use to fetch some data asynchronously via Ajax first, and once that's done, call itself again to manipulate the fetched data. This basically means calling the same instance of the constructor function again, but I can't get it to work. Here is a skeleton of what I'm trying to do:
function ajaxmenu(file){
var filefetched = false
var instance = this // save reference to this instance of ajaxmenu
if (!filefetched){
$.get(file, function( data ){
$(data).appendTo(document.body)
filefetched = true
instance() // how can I call instance again to initialize menu again now that ajax file is loaded?
return
})
}
this.menu = $('#menuid') // uses jQuery
this.menu.css({width: '100px'})
//do something else fancy with $menu
}
var menu = new ajaxmenu('menu.htm')
So basically the logic I want to happen here is, when ajaxmenu() is instantiated, the portion inside ajaxmenu() that fetches some file via Ajax is called, then once that's done, the same instance of ajaxmenu() is called again, but this time with the file in place already for the reminder of the function to parse and manipulate.
How can I do this? What I have now, calling instance() returns an error.
Generally speaking, the answer to your question Can I call an initialized object again is yes! You have many options.
If you insist to use the same function again, then one option would be to
add a second parameter filefetched to ajaxmenu
you do not need the var instance = this;
pass a true when calling the ajaxmenu from itself, to skip the fetching: ajaxmenu(file, true);
Full code:
function ajaxmenu(file, filefetched){
if (!filefetched){
$.get(file, function( data ){
$(data).appendTo(document.body)
ajaxmenu(file, true);
});
}
this.menu = $('#menuid') // uses jQuery
this.menu.css({width: '100px'})
//do something else fancy with $menu
}
var menu = new ajaxmenu('menu.htm', false);
Another option (without a self-call) would be to use the callbacks that jQuery offers with the get function and do your change to the menu in the done event, that is executed once the get function completes. This way you don't need the recursive call:
function ajaxmenu(file){
$.get(file, function( data ){
$(data).appendTo(document.body)
})
.done(function() {
this.menu = $('#menuid') // uses jQuery
this.menu.css({width: '100px'})
//do something else fancy with $menu
});
}
var menu = new ajaxmenu('menu.htm');
This also simplifies the code a lot, because you don't need branches (and/or recursive calls) and it is much more readable and so better maintainable.
Generally on recursive calls: you always need a condition to stop the recursion to prevent an infinite loop. One possibility would be using a parameter that is change on every new recursive call:
function process(data, n)
{
// process data
// iterate again or stop recursion
if (n > 0)
{
process(data, n - 1);
}
// done => n = 0
}
// start
process(data, 5);
Another option would be using a global variable and track it's state, but this generally indicates a bad design and it is not recommended:
// global variable
var n = 5;
function process(data)
{
// process data
// iterate again or stop recursion
if (n > 0)
{
n = n - 1;
process(data);
}
// done => n = 0
}
// start
process(data);
I want function A to finish execution and only after that function B should start executing. When I call function A and then function B, it seems both are executing simultaneously. And after function B completes, I want to call a third function update_dropdown().
My code looks like this:
function A {
for (var i = 0; i < 5; i++) {
var promise = $.get(url+i);
$.when(promise).then(function () {
$.post(url);
});
}
}
function B {
var x = $.get(url);
var promise = $.post(url+x);
$.when(promise0).then(function () {
update_dropdown();
});
}
Please can you tell me how I can make these 3 function calls happen sequentially.
OK, it's getting a little bit clearer what you actually want (based on your recent comments to address clarifying questions) though there are still at least two options open.
For an operation like this, you probably want to take advantage of a number of promise features:
jQuery's Ajax calls already return a promise so you can just use those directly
To serialize operations, you can just chain multiple promise operations together
To make async operations serialize properly, you can return a promise from a .then() handler and the master promise will resolve only when all the chained promises have resolved (kind of a built-in $.when() without having to explicitly call $.when()).
You can chain as many operations together as you want and the master promise will tell you when they are all done.
If you return promises from both A() and B(), then the callers of those functions can monitor when they are done with promise methods which then lets you chain A().then(B) to sequence those two.
When you sequence operations with chaining, the prior methods resolve data is passed to the next .then() handler function in the chain as the first argument to the .then() handler function so if you need the prior data for the next operation, it is right there to use.
So, with all those capabilities, it's just a matter of putting the right scaffolding around the code to implement the exact sequencing you want. Here are two different options:
Option 1: If you want to serialize everything in A() so that all 10 requests happen in serial fashion (the next one proceeds only when the prior one is done), then it could look like this:
// serialize all requests
function A() {
var p = $.get(url).then(function(data) {return $.post(url)});
for (var i = 1; i < 5; i++) {
// chain four more pairs of requests onto the original promise
p = p.then(function() {return $.get(url)})
.then(function(data) {return $.post(url)});
}
// return the promise so callers can monitor when A() is done
return p;
}
function B() {
// sequence these three operations one after the other
return ($.get(url)
.then(function(data) {return $.post(url + x)})
.then(update_dropdown)
);
}
// run them both, one after the other
A().then(B);
Option 2: If you want the 5 pairs of requests in A() to run in parallel, with only the last part of A() waiting until the 5 pairs of requests are done, then it could look like this:
// parallelize pairs of requests
function A() {
var promises = [];
for (var i = 0; i < 5; i++) {
// execute 5 pairs of requests where each pair is serialized in itself
promises.push($.get(url).then(function(data) {return $.post(url)}));
}
// return a promise that resolves only when all the other promises are done
return $.when.apply($, promises);
}
function B() {
// sequence these three operations one after the other
return ($.get(url)
.then(function(data) {return $.post(url + x)})
.then(update_dropdown)
);
}
// run them both, one after the other
A().then(B);
These use the concept that if you return a promise from a .then() handler function, then it will chain multiple async operations together and the master promise is only resolved when all the chained operations are resolved. This is very powerful for sequencing multiple ajax operations and you can even do it for operations in a loop like you have.
Something like this should work
function A {
var xhr = [];
for (var i = 0; i < 5; i++) {
xhr.push( $.get(url) );
}
$.when.apply($, xhr).then(B);
}
function B {
$.get(url).done(function(x) {
$.post(url + x).done(update_dropdown);
});
}
Note the use of an array to keep the promises in, then using $.when with apply() to fire a callback when all the ajax requests in the loop has finished.
Assumptions assumptions ...
Let's assume that :
the url for every get is the same as that for its corresponding post
the urls for each get-post pair should vary
the five get-post pairs in A can occur in parallel and we are not interested in the returned data
First, a utility function :
function getThenPost(url, appendToURL) {
return $.get(url).then(function(x) {
return (appendToURL) ? $.post(url + x) : $.post(url);
});
}
then A and B, both of which call the utility :
function A(urls) {
return $.when.apply(null, urls.map(function(url) {
return getThenPost(url, false);
}));
}
function B(url) {
return getThenPost(url, true);
}
and finally an expression that calls A and B :
A(['/path/0', '/path/1', '/path/2', '/path/3', '/path/4']).then(function() {
B('/path/5');
}).then(update_dropdown);
It should be reasonably simple to adjust this code if assumptions 1 and 2 are incorrect.
If assumption 3 is incorrect then A will require more extensive modification.
We can call our choice function in our way using jquery Deferred Object.
It is very simple let see successfully run example:
<body>
<script
src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script type="text/javascript">
// I want to call function in order of f1,f2,f3,f4 every time when i will execute this html page.
promise = f1().then(f2).then(f3).then(f4); // Add handlers to be called when the Deferred object is resolved, rejected, or still in progress.
function f1() {
var d = $.Deferred();
setTimeout(function() {
// our code here....
alert("1");
console.log("1");
d.resolve(); // resolve() :Resolve a Deferred object and call any doneCallbacks with the given args.
},1000); // You set some time for each method.
return d.promise(); //promise(): Return a Deferred’s Promise object.
}
function f2() {
var d = $.Deferred();
setTimeout(function() {
alert("2");
console.log("2");
d.resolve();
},1000);
return d.promise();
}
function f4() {
var d = $.Deferred();
setTimeout(function() {
alert("4");
console.log("4");
d.resolve();
},1000);
return d.promise();
}
function f3() {
var d = $.Deferred();
setTimeout(function() {
alert("3");
console.log("3");
d.resolve();
},1000);
return d.promise();
}
</script>
Javascript without extra work is single threaded. that means functions are not able to be executed simultaneously. but the problem is that the $.get() and $.post() calls are asynchronous. that means they are executed whenever the requested data arrives your client. (first come first serve)
an solution would be to execute function B after all the results ob A arrived, or to hold back all results and handle all data at once then run update_dropdown().
var UsersMenu = function(){
this.returnUsers = [];
this.retrieve = function(posts){
var temp = [];
$.post("router.php", { "action": "getUsersMenu", "posts" : posts},
function(data)
{
if(data.response){
for(var i=0; i<data.returnUsers.length; i++){
temp.push(data.returnUsers[i]);
}
this.returnUsers = temp; // i know what 'this' is incorrect
}
}, "json");
alert(this.returnUsers);
}
}
2 questions:
1. How to access to parent 'this' from jq object (returnUsers) ?
2. Why alert after jq post is calling before some alert in jq post ?
1 How to access to parent 'this' from jq object (returnUsers) ?
You could capture it in a closure:
var UsersMenu = function() {
this.returnUsers = [];
var self = this;
this.retrieve = function(posts) {
var temp = [];
$.post("router.php", { "action": "getUsersMenu", "posts" : posts },
function(data) {
if(data.response) {
for(var i = 0; i < data.returnUsers.length; i++) {
temp.push(data.returnUsers[i]);
}
self.returnUsers = temp;
}
}, "json");
}
};
2 Why alert after jq post is calling before some alert in jq post ?
Because AJAX is asynchronous. The $.post method which sends an AJAX request returns immediately but the success callback handler is executed much later, when a response is received from the server. So you shouldn't be putting the alert outside of this success handler. If you want to consume the results of an AJAX call this should happen only inside the callback which is when the result is available.
How to access to parent 'this' from jq object (returnUsers) ?
You should put the parent 'this' in a local variable like var self = this; outside the callback function, and then use self.returnUsers = temp;.
Why alert after jq post is calling before some alert in jq post ?
Because ajax works asynchronously, however for jQuery.ajax method, you can set it to work synchronously by async: false.
To answer your second question first: The $.post() function begins an asynchronous Ajax request. This means that the $.post() function itself returns immediately and execution continues with the next line of code which in this case is an alert(). Then once the Ajax request completes the anonymous function you provided to $.post() as a callback will be executed so if that function contain an alert() too it would be displayed then.
As to your first question: the value of this in a function depends on how a function was called, and jQuery typically sets it when it calls your callback functions but of course it won't be setting it to your UserMenu object. The easiest workaround is to save this in a variable that is local to your retrieve() function and then reference that variable from your callback:
var UsersMenu = function(){
this.returnUsers = [];
this.retrieve = function(posts){
var self = this,
temp = [];
$.post("router.php", { "action": "getUsersMenu", "posts" : posts},
function(data)
{
if(data.response){
for(var i=0; i<data.returnUsers.length; i++){
temp.push(data.returnUsers[i]);
}
self.returnUsers = temp;
}
}, "json");
alert(this.returnUsers);
}
}
Even though the retrieve() function will have finished by the time the Ajax callback is run the magic of JavaScript closures means that the inner anonymous function still has access to those local variables.
1 - Use a variable like that like
var that = this.returnUsers;
then inside the jq funciton you can refer to it like:
if(data.response){
for(var i=0; i<data.returnUsers.length; i++){
temp.push(data.returnUsers[i]);
}
that = temp; // Now 'this' is correct
}
2 - This becouse ajax calls are by default Asynchronous, means that javascript interpreter won't wait for the ajax call to be completed and it will continue executing the following statements, so put the alert in a callback function.
I can answer your second question.
Why alert after jq post is calling before some alert in jq post ?
Because AJAX is asynchronous, meaning you fire an AJAX request and don't wait for the outcome. You just register a callback function and carry on with the rest of the code. This "rest of the code" in your case is the alert statement.
It is highly unlikely (if not impossible) impossible for your AJAX response to arrive before the control reaches the alert statement.