Execute a function when a series of jquery ajax calls complete(!) - javascript

I need to make a series (1-20) ajax calls and I need to have another function be called when they are all complete. And by complete I mean when $.ajax({ complete: ...}) gets called.
Iv looked into using $.when, but after fiddling with it on jsfiddle I have 2 issues with it. 1. $.then and $.done gets called before all my complete callbacks.
2. if one single ajax call fails, it wont get called at all.
basically, it seems $.done is called on success, and not complete.
Im thinking there must be some good ajax manager/queue thingy out there that can handle this kind of stuff. Or maybe even some generic async task hanlding thingy.. ;)
The fiddle: http://jsfiddle.net/zvSgX/2

If you don't like the default jQuery choices for when $.ajax
calls resolve their deferred objects, you could write your own wrapper;
var my_ajax = function (options) {
var deferred = $.Deferred();
var user_complete = options.complete;
options.complete = function (jqXHR, textStatus) {
if (user_complete) user_complete(jqXHR, textStatus);
deferred.resolve();
};
$.ajax(options);
return deferred.promise();
};
Here is a fork of your JSFiddle which demos it in action with your
sample code.
Since my_ajax does not ever call deferred.reject(), chaining a .fail to $.when
will be meaningless if all the arguments to $.when are my_ajax calls.
Hope that is helpful! Please let me know if I can clarify anything.

You can use promise pattern to solve this problem
You can use when.js library to resolve this type of problem
Tutorial and samples are available at below location
https://github.com/cujojs/when

a solution I used recently worked not bad imo. Going through a for-loop I called a method which in turn did a window.setTimeout with a function executing an ajax call with the right data. I used a max and counter variable to check if all ajax calls where executed right (increment counter at the end of the success function). A function called by another setTimeout checked if the counter was equal to max. If not, call the method again in a new setTimeout, otherwise call the function that must be executed at the end.
So in code:
var count = 0, max = 0;
function batchCall() {
var a = [{
method: "DoThis",
params: { param1: 1, param2: 2 }
}, {
method: "DoThat",
params: { param1: 3 }
}]
max = a.length;
for (var i = 0; i < max; i++) {
callAjax(a[i]);
}
window.setTimeout(checkAllFinished, 100);
}
function callAjax(o) {
window.setTimeout(function() {
// do ajax call here
}, 0);
}
function checkAllFinished() {
if (count == max) {
// do what you need to do when all are called
}
else {
window.setTimeout(checkAllFinished, 100);
}
}

Related

json after success return undefined

I am using JS with Angular came to the following problem.
I am getting data from a requestService, so a call the request function and then do something with the data in a 'success' function. Inside this function, everything is fine and I get all my results but as soon as a leave the success function, my results are undefined. I read some other questions/answers about similar problems, and tried other things. However I do not really know how to hand this and wanted to ask this explicitly with a code example:
function loadShips() {
var count = 0;
RequestService.getShips(nelat, swlat, nelong, swlong, timestamp)
.success(function(results) {
var groupedShips = results.aisData.aisGroupedByShipType;
_.each(groupedShips, function(groupedShip) {
_.each(groupedShip, function(ship) {
Markers['marker' + count] = createMarker(ship);
count++;
});
});
console.log(Markers, '#1')
return Markers
});
console.log(Markers, '#2');
return Markers;
}
So anyone could maybe tell me, why Markers at the print out of '#1' are defined and at '#2' are undefined.
Assuming the request is being done asynchronously, the call at #2 is happening before the request's success method is being called. This would explain why the object does not exist yet.
One solution would be to pass a call back method into the factory as a parameter, and then call that method after the request success has occurred. That would look something like this:
function loadShips(callBack) {
var count = 0;
RequestService.getShips(nelat, swlat, nelong, swlong, timestamp)
.success(function(results) {
var groupedShips = results.aisData.aisGroupedByShipType;
_.each(groupedShips, function(groupedShip) {
_.each(groupedShip, function(ship) {
Markers['marker' + count] = createMarker(ship);
count++;
});
});
console.log(Markers, "#1");
callBack(Markers);
});
}
Using this method looks like this:
function myCallback(markers){
console.log(markers, "#2");
//assign markers to something
}
loadShips(myCallback);
As Will P pointed out, with asynchronous functions, the inline code after them will execute first, because the success function is still waiting in the event queue.
in addition to that, Markers is being returned from inside a anonymous function which will not return it as the result of loadShips but will return it inside ajax wonderland, never to be seen.
what you will have to do is have a function that receives the data when it is ready and call that function with the data. I'm assuming things happen after the ships load, those things will have to be called after the anonymous function is done creating Markers.
function loadShips() {
var count = 0;
RequestService.getShips(nelat, swlat, nelong, swlong, timestamp)
.success(function(results) {
var groupedShips = results.aisData.aisGroupedByShipType;
_.each(groupedShips, function(groupedShip) {
_.each(groupedShip, function(ship) {
Markers['marker' + count] = createMarker(ship);
count++;
});
});
doFancyWonderfulThingsInTheOcean(Markers);
});
}

Javascript: Call a function after all async calls have returned

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
});
}

Increment for only after the previous interaction has been finished (callback)

I'm having a problem with callback functions in javascript. What I want to do is: loop on a for and call a function passing i as parameter. With that in mind, I have to loop to the next interaction only after the previous one has been finished. I don't know if this is a problem but inside the function I'm sending i as parameter, I have another callback function. Here is my code:
for(i=0; i<10; i++) {
aux(i, function(success) {
/*
* this should be made interaction by interaction
* but what happens is: while I'm still running my first interaction
* (i=0), the code loops for i=1, i=2, etc. before the response of
* the previous interaction
*/
if(!success)
doSomething();
else
doSomethingElse();
});
}
function aux(i, success) {
... //here I make my logic with "i" sent as parameter
getReturnFromAjax(function(response) {
if(response)
return success(true);
else
return success(false);
});
});
function getReturnFromAjax(callback) {
...
$.ajax({
url: myUrl,
type: "POST",
success: function (response) {
return callback(response);
}
});
}
jQuery's Deferred can be a bit tricky to get right. What you'll have to do is stack your promises in a chain. For example:
var
// create a deferred object
dfd = $.Deferred(),
// get the promise
promise = dfd.promise(),
// the loop variable
i
;
for(i = 0; i < 10; i += 1) {
// use `then` and use the new promise for next itteration
promise = promise.then(
// prepare the function to be called, but don't execute it!
// (see docs for .bind)
aux.bind(null, i, function(success) {
success ? doSomethingElse() : doSomething();
})
);
}
// resolve the deferred object
dfd.resolve();
for this to work, aux must also return a promise, but $.ajax already does this, so just pass it through and everything should work:
in aux:
function aux(i, callback) {
console.log('executing for `aux` with', i);
// return the ajax-promise
return getReturnFromAjax(function(response) {
callback(Boolean(response));
});
}
in getReturnFromAjax:
function getReturnFromAjax(callback) {
// return the ajax-promise
return $.ajax({
url: '%your-url%',
type: '%method%',
success: function (response) {
callback(response);
}
});
}
demo: http://jsbin.com/pilebofi/2/
I'd suggest that you'd look into jQuery's Deferred Objects and jQuery.Deferred()-method instead of making your own callback queue functions (as you are already using jQuery anyway).
Description: A constructor function that returns a chainable utility
object with methods to register multiple callbacks into callback
queues, invoke callback queues, and relay the success or failure state
of any synchronous or asynchronous function.
I don't have experience with jQuery, but your callback looks a bit fishy to me.
In plain JS I'd suggest trying something among the lines of this:
function yourMainFunction
{
function callbackHandler(result)
{
// Code that depends on on the result of the callback
}
getAjaxResults(callbackHandler);
}
function getAjaxResults(callbackHandler)
{
// Create xmlHttpRequest Handler, etc.
// Make your AJAX request
xmlHttp.onreadystatechange = function()
{
if (xmlHttp.readyState == 4 && xmlHttp.status==200)
{
// Do stuff you want to do if the request was successful
// Define a variable with the value(s) you want to return to the main function
callbackHandler(yourReturnVariable);
}
}
}

Using setTimeout and recursion to see if something is "ready"?

I'm trying to figure out a good way to check if some asynchronous call is "ready" or not. I have some function that runs $.ajax, and in the callback function sets a boolean variable in the global scope to true (along with some other stuff). Before the ajax call, that boolean variable is false.
I want another function that retrieves that "other stuff." Since the ajax call is asynchronous, clearly I can't just immediately go retrieving it because it probably won't be there yet. That's where this boolean variable comes in. I'm thinking I can just check if that boolean is true every 100ms, and when it is, THEN go retrieve and return the "other stuff".
Code-wise, it looks something like this:
window.FOO = window.FOO || {};
;(function() {
var isReady = false;
var stuff;
$.ajax({
...
success: function(data) {
stuff = data;
isReady = true;
}
})
FOO.getStuff = function() {
// How to check if it's "ready"?
};
}
... (somewhere else)...
var stuff = FOO.getStuff();
I've tried the following for FOO.getStuff to no avail, because (I think) setTimeout is asynchronous:
FOO.getStuff = function() {
if (isReady) {
return stuff;
}
var theStuff;
setTimeout(function() {
theStuff = FOO.getStuff();
}, 100);
return theStuff;
};
Using the console, I can see it doing the right thing... but the first FOO.getStuff call returns before the subsequent ones do.
Is there a better way of doing this?
Edit: To clarify, I want the ajax call to remain asynchronous. I'm perfectly fine with the getStuff() calls being synchronous, because the ajax call will be very fast, and in most cases, getStuff() will be called later (after the used does some things).
Per your comments I have your answer. To solve async problem we should do async actions.
var stuff;
var stuffQueue = [];
$.ajax({
success: function(data) {
stuff = data;
if( stuffQueue.length > 0 ){
for(var i in stuffQueue){
var callback = stuffQueue[i];
callback(stuff);
}
stuffQueue = [];
}
}
});
function getStuff(callback){
//stuff has been loaded?
if( stuff ){
callback(stuff);
}else{
stuffQueue.push(callback);
}
}
To get stuff invoke:
var something = getStuff(function(stuff){
console.log(stuff);
});
This should solve your use case. Let me tell you more info, I have a JavaScript template engine, not yet open source but I have been using in professional projects, and I load all the templates with just one HTTP request, I made that request async:false because it is a dependence of the project.
There are reads that said that async false is evil, I do not believe so, what is evil is to use it wrong. Loading a templates file master, is a good example where async:false could work.
Additional I recommend you to read about promisses:
http://www.html5rocks.com/en/tutorials/es6/promises/
similar idea with Danie Aranda, I'd like to sugget you use custom event.
var isReady = true;
$.ajax({
beforeSend: function() {
isReady = false;
},
success: function(data) {
isReady = true;
$(document).trigger('display-stuff', data);
}
});
Foo.getStuff = function(data) {
if (!isReady) {
$(document).one('display-stuff', Foo.getStuff);
return;
}
// do something
};

jQuery Ajax How do callbacks work?

Hello fellow programmers! I just started an additional programming project and swore to god my code will bo SO much cleaner and easily upgradeable than it has been before.
Then I stumbled upon my "arch enemy" the jQuery AJAX returning. Last time I wanted to return something from an AJAX call I had to bend over and just make the call synchronous. That made things sticky and ugly and I hope that this time I will find something better.
So I have been googling/searching stackoverflow for a while now, and just don't understand this solution many ppl has gotten which is called callback function. Could someone give me an example on how I could exploit these callback functions in order to return my login statuses:
function doLogin(username, password) {
$.ajax({
url: 'jose.php?do=login&user='+username+'&pass='+password,
dataType: 'json',
success: function(data) {
if(data.success==1) {
return('1');
} else {
return('2');
}
$('#spinner').hide();
},
statusCode: {
403:function() {
LogStatus('Slavefile error: Forbidden. Aborting.');
$('#spinner').hide();
return (3);
},
404:function() {
LogStatus('Slavefile was not found. Aborting.');
$('#spinner').hide();
return (3);
},
500:function() {
LogStatus('Slavefile error: Internal server error. Aborting.');
$('#spinner').hide();
return (3);
},
501:function() {
LogStatus('Slavefile error: Not implemented. Aborting.');
$('#spinner').hide();
return (3);
}
},
async: true
});
}
So as you probably know, you cannot use return the way I have done from inside an AJAX call. You should instead use callback functions which I have no idea of how to use.
I'd be VERY greatful if someone could write me this code using callback functions and explaining to me just HOW they WORK.
EDIT:
I REALLY need to return stuff, not use it right away. This function is being called from within another function and should be able to be called from different places without being rewritten even slightly.
/EDIT
Sincerly,
Akke
Web Developer at Oy Aimo Latvala Ab
There are three parts to the basic "I need an asynchronous callback" pattern:
Give the function a callback function parameter.
Call the callback function instead of returning a value.
Instead of calling the function and doing something with its return value, the return value will be passed to your callback function as a parameter.
Suppose your synchronous mind wants to do this:
function doLogin(username, password) {
// ...
return something;
}
switch(doLogin(u, p)) {
case '1':
//...
break;
case '2':
//...
break;
//...
}
but doLogin has to make an asynchronous call to a remote server. You'd just need to rearrange things a little bit like this:
function doLogin(username, password, callback) {
return $.ajax({
// ...
success: function(data) {
if(data.success == 1)
callback('1');
else
callback('2');
},
//...
});
}
var jqxhr = doLogin(u, p, function(statusCode) {
switch(statusCode)) {
case '1':
//...
break;
case '2':
//...
break;
//...
}
});
The jqxhr allows you to reference the AJAX connection before it returns, you'd use it if you needed to cancel the call, attach extra handlers, etc.
A callback is simply a function that runs when certain conditions are met. In this case, it is when ajax has a "success".
You are already using a callback, but you don't recognize it. success: function(data) {} is a callback, but it's just what's called an anonymous function. It has no name or reference, but it still runs. If you want to change this anonymous function to a named function, it is really simple: take the code in the anonymous function, and put it in a named one, and then just call the named one:
[...]success: function(data) {
if(data.success==1) {
return('1');
} else {
return('2');
}
$('#spinner').hide();
}, [...]
should change to:
[...]success: function(){ callbackThingy(data) }, [...]
And now just create the callbackThingy function:
function callbackThingy(data){
if(data.success==1) {
someOtherFunction('1');
} else {
someOtherFunction('2');
}
$('#spinner').hide();
}
Note that the "return" value does nothing. It just stops the callback function, whether you are in an anonymous function or a named one. So you would also have to write a second function called someOtherFunction:
function someOtherFunction(inValue){
if(inValue=='1') {
// do something.
} else if(inValue=='2') {
// do something else.
}
}
The above example is if you have to pass parameters. If you do not need to pass parameters, the setup is simpler:
[...]success: callbackThingy, [...]
function callbackThingy(){
// do something here.
}
From the edit in your original post, I can see that you just need to store a (more) global variable. Try this:
// in the global scope , create this variable:
// (or -- at least -- in the scope available to both this ajax call
// and where you are going to use it)
var valHolder = -1;
// then edit your ajax call like this:
[...]
success: function(data) {
if(data.success==1) {
valHolder = 1;
} else {
valHolder = 2;
}
$('#spinner').hide();
},
[...]
Now you can verify 3 things:
valHolder = -1 means that the ajax call has not yet returned successfully
valHolder = 1 means data.success = 1
valHolder = 2 means data.success != 1.
Another option is to store the variable in an HTML attribute of some element.
Finally, you should probably look at jquery.data for the most jquery way of managing stored data.
Does this help?
Just as a small point of interest, you don't have to include
async : true;
as part of your $.ajax options. The default setting for async is already "true".
Sorry to post this as a response, but until I have 50 rep I can't make a simple comment. (Feel free to help me out with that! ^_^ )

Categories

Resources