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);
});
}
Related
I am trying to call a function to get a value from a 'subproduct' table and insert it in to another table. However the value which I am returning is not fetching the latest value from table and it is getting returned even before the snapshot part of the function is getting executed. I want it to run synchronously. Is there a better way in which it can be written.
function getGSTvalues(para1) {
var gstVar = 1;
var gstVarPromise = SubProductRef.once("value").then(function(snapshot) {
snapshot.forEach(function(child) {
if (para1 == child.val().subproductName) {
gstvar = child.val().gst;
console.log("InsidePromise" + gstVar);
}
});
console.log("outside fun : " + gstVar);
});
console.log("outside fun1 : " + gstVar);
return gstVar;
};
This is where I am calling the above function:
var gstans = getGSTvalues($('#edit_ProductSubType').val());
Any help would be appreciated
Using synchronous logic would be a big step backwards. The best solution here would be to use the asynchronous pattern correctly and provide a callback function to getGSTvalues() which is executed after the async operation completes and receives the result as an argument. Try this:
function getGSTvalues(para1, cb) {
var gstVar = 1;
var gstVarPromise = SubProductRef.once("value").then(function(snapshot) {
snapshot.forEach(function(child) {
if (para1 == child.val().subproductName) {
gstVar = child.val().gst;
}
});
cb && cb(gstVar);
});
};
getGSTvalues($('#edit_ProductSubType').val(), function(gst) {
console.log(gst);
// work with the value here...
});
Another alternative would be to return the promise from SubProductRef from getGSTvalues() and apply then() on that in the calling scope, although this would render the function largely redundant.
Also note that JS is case sensitive so gstVar is not the same as gstvar. I corrected this above.
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 nested set of ajax calls, something like:
function getSubcategories(cat,callback) {
$.ajax({
url:'myurl.php',
data:'q='+cat,
dataType='json',
success:function(result){ callback(result) }
});
}
function getSubcatElements(subcat,callback) {
$.ajax({
url:'myurl2.php',
data:'q='+subcat,
dataType='json',
success:function(result){ callback(result) }
});
}
function organizeData(cat,callback) {
getSubcategories(cat,function(res){
totals=0;
list=new Array;
$.each(res['subcat'],function(key,val){
getSubcatElements(val,function(items){
$.each(items['collection'],function(key2,val2) {
list.push(val2['descriptor']);
});
totals+=items['count'];
// If I shove "totals" and "list" into an object here to callback, obviously gets called many times
}
// If I return an object here, it doesn't actually have counts from the asynchronous call above
}
function doStuff(cat) {
organizeData(cat,function() {
//stuff
});
So I'm running a looped asynchronous query that's a child of another asynch query, and I want the final result of the child loop without being "lazy". Right now I have it just returning updated results so the numbers change a few times, but I'd like to do it in one fell swoop.
It seems that the obvious place to do it would be to store the results in the asynch and return it after the $.each(), but JavaScript is insane and scoffs at things like obviousness. I feel like this should involve $.Deferred() but the samples I found all seemed like they should trigger after the first iteration ...
(The functions are deliberately separated as there is sometimes reason to use only one or only the other).
Thanks in advance!
Right now, your approach is fine. I want to add following changes in your code
function organizeData(cat, callback) {
getSubcategories(cat, function(res) {
totals = 0;
list = new Array();
totalSubCatItem = res['subcat'].length;
currentSubCatItem = 0;
$.each(res['subcat'], function(key, val) {
getSubcatElements(val, function(items) {
$.each(items['collection'], function(key2, val2) {
list.push(val2['descriptor']);
});
totals += items['count'];
// If I shove "totals" and "list" into an object here to callback, obviously gets called many times
// Here the solution
currentSubCatItem++;
if(currentSubCatItem === totalSubCatItem){
callback(/** pass argument here **/)
}
});
// If I return an object here, it doesn't actually have counts from the asynchronous call above
});
})
}
function doStuff(cat) {
organizeData(cat, function( result) {
//stuff
console.log(result)
});
}
First, you should probably organize your database query on the server side so it's returning a single, multi-plexed result. Rather than calling it lots of times.
Barring that, and assuming you don't know how many sub-categories you're going to call until your category call returns, your best bet is to create a global var that counts up every time it makes a call, and then counts down every time the callback receives a result. Whenever callback fires, counts down, and the new count is zero, do your updates.
Here is my piece of code am trying:
initData:function(period)
{
this.getLoadObject('0',url, this.callbackFunction);
},
I want to receive a return value from callBackFunction, which I want to be returned by initData.
Can any body help me, am not so proficient with JS.
I tried similar question but didn't get help.
Actually, the callback function may very well be called asynchronously, that is after the return from the call to initData.
If it is synchronous however, you can do this:
initData:function(period)
{
var that = this;
var result;
this.getLoadObject('0',url, function() {
result = that.callbackFunction(arguments));
});
return result;
},
Although it's impossible to tell from your code snippet, it looks like your callback function will be called asynchronously. if this is the case you will need to set variables that you want to be populated by initData within the callback function itself.
you could also wrap the callback function:
var context = this;
this.getLoadObject('0',url, function() {
var returnedval = context.callbackFunction.apply(context,arguments);
context.SomeFuctionToUseValue(returnedval);
});
I think you could use variable outside the function, so you can access it inside the callback function and outside.
var result = false;
initData:function(period)
{
this.getLoadObject('0',url, this.callbackFunction);
//inside the callback you can modify the "result" then
return result;
},
When the form is submitted, I'm calling a function getPosts and passing through a variable str. What I'd like to do is get the data returned from that function.
// when the form is submitted
$('form#getSome').submit(function(){
var str = $("form#getSome").serialize();
var something = getPosts(str);
* This is where I'd like to get the data returned from getPosts()
return false;
});
// get the data
function getPosts(str){
$.getJSON('http://myurl.com/json?'+str+'&callback=?',
function(data) {
arrPosts = new Array();
$.each(data.posts, function(i,posts){
// build array here
});
return arrPosts;
});
};
I've tried many things, but have only gotten 'undefined' returned. I've tried console.log(something);, console.log(getPosts).
I'm missing something very fundamental here. Any help would be greatly appreciated.
EDIT:
What I'm trying to do is create a single function that would get posts. Then different events would call that function. I could then use that data. So one event may be submitting a form, another may be clicking a link, another lazy/endless scrolling. All could use the same getPosts function.
There's a lot of parsing out the results which amounts to a lot of lines of code. Was just trying to find a way to reuse that function. Do you think that would be possible?
$('a.thisLink').click(function(){
getPosts();
get the return from getPosts() and do something with it
});
$('form.thisForm').submit(function(){
getPosts();
get the return from getPosts() and do something with it
});
function getPosts(){
get the posts and return an array
}
Ajax requests are executed asynchronously, the callback function (function (data)) of getJSON is executed when the request ends, and returning a value in that callback has no effect, because is a nested function inside getPosts and its return value is never used.
Actually in your example, getPosts doesn't return anything and it ends its execution before the data is returned.
I would recommend you to work on your submit event handler, if you want to keep the getPosts function, you can introduce a callback parameter:
$('form#getSome').submit(function(){
var str = $("form#getSome").serialize();
getPosts(str, function (data) {
var array = [];
$.each(data.posts, function(i,posts){
// build array here
array.push(/* value to add */);
});
// array ready to work with...
//...
});
return false;
});
function getPosts(str, callback){
$.getJSON('http://myurl.com/json?'+str+'&callback=?', callback);
}
Edit 2: In response to your second comment, you could make another callback, that will be executed when the data has been processed by the first callback, and you can define it when you execute the getPosts function on the submit event handler:
$('form#getSome').submit(function(){
var str = $("form#getSome").serialize();
getPosts(str, reusableCallback, function (result) {
// result contains the returned value of 'reusableCallback' <---
});
return false;
});
function reusableCallback(data) {
var array = [];
$.each(data.posts, function(i,posts){
array.push(/* value to add */);
});
//...
return array;
}
function getPosts(str, callback, finishCallback){
$.getJSON('http://myurl.com/json?'+str+'&callback=?', function (data) {
finishCallback(callback(data)); // pass the returned value
// of callback, to 'finishCallback' which is
// anonymously defined on the submit handler
});
}
Edit 3: I think that the getPosts function and the "reusableCallback" function are strongly related, you might want to join them, and make the code easier to use and understand:
$('form#getSome').submit(function(){
var str = $("form#getSome").serialize();
getPosts(str, function (result) {
// result contains the processed results
});
return false;
});
function getPosts(str, finishCallback){
$.getJSON('http://myurl.com/json?'+str+'&callback=?', function (data) {
// process the results here
var array = [];
$.each(data.posts, function(i,posts){
array.push(/* value to add */);
});
//...
finishCallback(array); // when the array is ready, execute the callback
});
}
Your getPosts function looks incomplete, I'm no jquery expert but should it look something like:
function getPosts(str) {
$.getJSON('http://myexample.com/json?'+str+'&callback=?',function(data){
var arrPosts = [];
$.each(data.posts, function(i,posts){
... build array yada yada ...
});
return arrPosts;
});
}
The problem is that the $.getJSON callback function gets called when the get request returns the data, not inline with your function.