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.
Related
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
}
})
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);
});
}
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 some code that requests some JSON from an API. When data is returned, per the documentation, it sends back a callback function that is to be used to parse the data at the top level. After the call is made, I have the following code to capture the data and process it:
var callback = 'functionUsedInApiCall';
window[callback] = newCallBackFunction;
How would I go about passing custom params to the callback function above as the data is being returned?
In order to capture the data, I must write the callback function like this:
function newCallBackFunction(root) {
//root is the data
}
Any help would be greatly appreciated.
Are you talking about JSONP? If so, you don't call the callback or pass in the argument at all, the code returned by the API does.
E.g., your code:
window.myCallback = newCallbackFunction;
function newCallbackFunction(data) {
// use the data
}
(I'm assuming this isn't at global scope, hence assigning to the window object.)
...plus your code for initiating the JSONP call, which is usually appending a script element to your page with a URL containing the name of the callback ("myCallback" in the above).
Their response will look like this:
myCallback({
// data here
});
...which, when it arrives, will run (because it's the content of a script element), and will call your function. This is how JSONP works.
If you want to include further arguments for the function, all you do is have the callback they call turn around and call your target function, e.g.:
window.myCallback = function(data) {
newCallbackFunction(data, "foo", "bar");
};
function newCallbackFunction(data) {
// use the data
}
Now when their code calls the global myCallback, all it does is turn around and call newCallbackFunction with that data and the arguments you specify.
Those arguments don't have to be literals as in the above. Here's an example with a bit more context, using a closure:
// Assume the url already contains the name of the callback "myCallback"
function doJSONP(url, niftyInfo, moreNiftyInfo) {
var script;
// Set up the callback
window.myCallback = function(data) {
// Runs when the data arrives
newCallbackFunction(data, niftyInfo, moreNiftyInfo);
};
// Trigger the request
script = document.createElement('script');
script.src = url;
document.documentElement.appendChild(script);
}
Ideally, though, when doing JSONP you auto-generate the name of the callback each time so that it's specific to the request (in case you have two outstanding requests at the same time):
// Assume the url ends with "callback=" and we append the name of the
// callback function to it
function doJSONP(url, niftyInfo, moreNiftyInfo) {
var cbname, script;
// Get a callback name
cbname = "callback_" +
new Date().getTime() +
"_" +
Math.floor(Math.random() * 10000);
// Set up the callback
window[cbname] = function(data) {
// Remove us from the window object
try {
delete window[cbname];
}
catch (e) { // Handle IE bug (throws an error when you try to delete window properties)
window[cbname] = undefined;
}
// Runs the function
newCallbackFunction(data, niftyInfo, moreNiftyInfo);
};
// Trigger the request
script = document.createElement('script');
script.src = url + encodeURIComponent(cbname);
document.documentElement.appendChild(script);
}
Parameters in javascript are passed as an Array, so you can pass the parameters you need, or even complete functions that will add per case functionality in your callback.
You could do the following:
function newCallBackFunction(root /*your data*/, paramsHash /*a hash array with optional parameters*/)
{
//if arg1 can be found in the hash
if( paramsHash['arg1'] ]
{
//do something that requires arg1
}
else if( paramsHash['arg2'] )
{
//do something that requires arg2
}
return root;
}
And in your main code:
var hash = new Array();
hash['arg1'] = 'str1';
hash['arg2'] = 1;
hash['arg3'] = new Car(); //or any other object you want
It is also possible to just declare some parameters and supply them to your function only when needed:
function newCallBackFunction(root, param1, param2)
{
if( param1 ) { /* similar to first example */ }
}
Or finally just pass whatever parameter you want and read them from the arguments table
function newCallBackFunction(root)
{
for( int i = 1; i < arguments.length; i++ )
//do something with the parameters you pass beside root
}
And in main code:
newCallBackFunction( root, param1, param2, param3 );
I hope I covered you!
Perhaps this has already been addressed, but couldn't find a suitable answer here. Apologies if it's the case. Here's the issue :
function Aclass(){
this.value1 = "a first value"
this.value2 = pulldata()
function pulldata(){
$.getJSON("data.json",function(data){
return data
})
}
}
var obj = new Aclass()
console.log(obj.value1)
console.log(obj.value2)
Result : "a first value" then "undefined"
The data var isn't available out of the pulldata's scope, and I'm not sure what's the best way around this.
Your pullData() function doesn't actually return anything explicitly, so its return is undefined - which means what you are seeing when you log obj.value2 is correct (though undesired).
Inside pullData() you are calling $.getJSON() and passing it an anonymous function as a callback, and it is that anonymous function that returns data but the return doesn't go anywhere because the ajax call is asynchronous and the callback doesn't occur until later.
You could try this:
function Aclass(classInitialisedCallback){
var self = this;
this.value1 = "a first value";
this.value2 = "not available yet";
$.getJSON("data.json",function(data){
self.value2 = data;
if (typeof classInitialisedCallback === "function")
classInitialisedCallback(self);
});
}
var obj = new Aclass(function(newInstance) {
// can use obj or newInstance to refer to the object
console.log(obj.value1);
console.log(newInstance.value2);
});
That is, set up your Aclass() constructor to take a parameter that is a callback function to be executed when the class is ready, i.e., after the result from $.getJSON() is available.
$.getJSON makes an asynchronous ajax call to your "data.json" URL. The second argument you've given it is the callback, which is called when the ajax call is finished.
Your pulldata function, however, just runs through without waiting for the answer and, thus, returns undefined.
It's not possible to do exactly what you're trying to do with asynchronous calls (and you should never use synchronous calls).
You'll have to rewrite your code to do your stuff in the callback instead. The callback is your "function(data){}" argument.
If the function you called wasn't asynchronous however, this is how the JS scope works and should've solved your problem:
function Aclass() {
var that = this;
this.value2 = 'not set';
var pullData = function() {
someCallToSomethingWithACallback(function() {
that.value2 = 'set';
}
}
}