I am new to javascript, and can't find the solution to this.
I have read some of the similar questions, but did not look like the problem was the same as mine.
I call a method from script1 with this code:
function turnPage(){
var current = window.now;
var nextpage = getNextPage(current);
alert(nextpage);
}
In script2 there is a SQLite etc:
function getNextPage(Pid) {
db.transaction(function(tx) {
tx.executeSql('SELECT * FROM Page WHERE Pid=' + Pid, [],
function(tx, results) {
nextp = parseInt(results.rows.item(0).NextPage);
//alert(nextp);
return nextp;
}, errorCB);
}, errorCBQuery);
}
if I use the alert-dialog in the called function, the variable nextp is 2.
BUT if I return the variable, it will alert as 'undefined'.
Also, if I etc make the variable var nextp = 11; over "db.transaction..." and the return-statement at the end of the function, it will return 11 instead of 2.
Is it because the variable is not sent to the inner function in my inception of functions? :)
Any ideas of what to do? thanks!
I don't know how SQLite in javascript works, but I suspect it works asynchronously, so you're calling alert in turnPage but the transaction is running async and the return value is in another scope anyway. You can try passing a callback function to getNextPage and then instead of returning nextp call the callback with nextp as argument:
function turnPage(){
var current = window.now;
getNextPage(current, function (nextp) { alert(nextp); /* do whatever else you need to do */ });
}
function getNextPage(Pid, cb) {
db.transaction(function(tx) {
tx.executeSql('SELECT * FROM Page WHERE Pid=' + Pid, [],
function(tx, results) {
nextp = parseInt(results.rows.item(0).NextPage);
cb(nextp);
}, errorCB);
}, errorCBQuery);
}
Your outer function never actuallay returns nextp at any time.
There are two solutions to what (I think) you are going to do
Create a global variable and store the value of nextp there.
Introduce another callback to use the value of nextp, e.g., create a new link or whatever. If you want, you can put the code either in another function and pass it as a parameter to getNextPage() or put the code directly into the most inner function (at the position of your alertcall).
Note that callbacks are used to handle the asynchronous nature of many JavaScript APIs. So even your getNextPage() can't return the value of the inner query as that value is not present, when getNextPage() is finished.
Anyway the return statement in the most inner function can be dropped as there is no function to actually receive that value.
As it is now, there are several problems with your code.
As JavaScript mostly is asynchronus, your code:
nextpage = getNextPage(current);
alert(nextpage);
Will call getNextPage() but it will not wait for the response before moving on, so the alert will be fired right away, and at that point, if the response hasn't been returned and assigned (which is likely to be the case), then nextpage will be undefined.
Your second problem is that your outer function does not return anything. You return the value from your inner function, but then "it get stuck" in your outer function. One solution would be to assign the value to a global variable, instead of returning it from the function. You would still have to look out for the "asynchronous-problem", so that you don't read the global variable until it has been assigned.
Related
I have been trying to set up callbacks to get the results from an asynchronous operation, but I have been unsuccessful so far. Have a look at the structure of the code below.
var markers = []; //global array
//The callback parameter is a reference to the function which is passed as an argument from the doAsyncOperation call
function doAsyncOperation(callback) {
var xmlArray = []; //the array that will hold the data that I'm trying to access later in the code
downloadUrl("theXmlFile.xml", function (data) { //this is the async code that stores the results in an array called xmlArray
var xmlItems = data.documentElement.getElementsByTagName("row");
xmlArray.push(theData); //this array is populated with data within this async code block
//the logic works here; this part is not the issue
});
setTimeout(function () {
callback(xmlArray); //passing xmlArray as the result
}, Math.random() * 2000);
}
//the code below is the function call that should be getting the results from the doAsyncOperation function
doAsyncOperation(function (xmlData) {
alert(xmlData); //I am not able to see the data in xmlArray here
});
//the function below is called on Window.onload
function initialize() {
//In this function, I need to use the results in the xmlArray above.
//I have tried saving the results into a global array, but it doesn't work because this function is called
//before the async operation completes.
}
To sum it up, I am having trouble accessing the results of an asynchronous operation. As you can see, I have tried to set up callbacks to access the data, but I have to be doing something wrong. I have looked at similar questions here, but none of them seem to address the issue I'm having specifically. Any help is appreciated.
You have two variables called xmlArray.
One is in the scope of doAsyncOperation.
The other is in the scope of the anonymous function you pass as an argument to downloadUrl.
Both of them start out by having empty arrays assigned to them.
The second one has some items pushed into it.
The first one is the one you pass to callback
Remove the line
var xmlArray = []; //this array is populated with data within this async code block
If you want the code in the anonymous function to modify the first array instead.
NB: Since you try to deal with the data Math.random() * 2000 after sending the request, it is possible that you will not have received a response (triggering the anonymous function) before you try to pass it to callback.
Shouldn't this be like shown below - the callback is invoked after downloadUrl finishes
function doAsyncOperation(callback) {
var xmlArray = [];
downloadUrl("theXmlFile.xml", function (data) {
var xmlItems = data.documentElement.getElementsByTagName("row");
xmlArray.push(theData);
callback(xmlArray);
});
}
doAsyncOperation(function (xmlData) {
alert(xmlData);
});
I've got some experience in PHP, but I'm starting out with javascript and jquery. I'm working on my first project. I thought that scripting is scripting, and there will be little difference between this and PHP. Well was I wrong. For the first time I saw that something which is first in the code executes last!
Please have a look at this function which is meant to get svg and store them in json object to use as inline svg later
var svgIcons = { "arrow_left": "", "arrow_right":"", } //json object with empty values
this.getIcons = function() {
for (var icon_name in svgIcons) {
if (svgIcons.hasOwnProperty(icon_name)) {
var url=PHP.plugin_url+'/includes/icons/'+icon_name+'.svg';
jQuery.get(url, function(data) {
svgIcons[icon_name]=data;
console.log('iterating');
console.log(svgIcons[icon_name]); //outputs svg
});
}
}
console.log('this should be after iteration');
console.log(svgIcons["arrow_left"]); //empty
}
this.getIcons(); //called at object initialization
But the output is:
this should be after iteration
iterating
#document (and svg inside it)
iterating
#document (and svg inside it)
What is the cause of this change of order? Is it the get() function? How do I avoid situations like this?
jQuery.get is asynchronous. You are iterating inside the callback for an AJAX call, so that gets executed whenever the AJAX call is completed.
AJAX callbacks, setTimeout and setInterval are some asynchronous Javascript functions. Some threads you might find useful:
How does Asynchronous Javascript Execution happen?
Are all javascript callbacks asynchronous? If not, how do I know which are?
Edit: Yes, the function call ends before any of the callback stuff happens. Basically the execution of your JS will be linear, placing functions on a call stack whenever they are called. On the call-stack they are executed one-by-one, line-by-line. However, when one of those lines calls an asynchronous function (like a setTimeout or AJAX), the current execution places the async function on the call-stack and immediately returns to complete itself. So something like:
function myFunc(){
console.log('a');
setTimeout(function(){
console.log('b');
},0)
console.log('c');
}
myFunc();
would always log:
a
c
b
...even though the setTimeout is 0.
So, in your case what must be happening is that you are assigning the AJAX-received data to svgIcons[icon_name] inside the async callback (obviously), while the rest of your code which uses the object svgIcons is in the sequential/normal execution. You either have to move the code that uses the object inside the async callback, or use promises (basically promises are functions that are executed after an async call is completed).
2nd Edit: So, the reason you are not able to set svgIcons[icon_name] inside the callback is related to the things I was mentioning in my comment. When synchronous functions are called, they are placed on top of the current stack and executed right away, before returning to the calling function. So if you called a sync function inside a loop:
function outer(){
function inner(){
console.log(i);
}
for(var i=0;i<3;i++)
inner();
}
outer();
the synchronous inner function would be executed right away inside each loop, and would have access to the current value of i, so it would output 0, 1, 2 (as expected).
If however, inner was asynchronous, e.g
function outer(){
for (var i=0;i<3;i++)
setTimeout(function(){console.log(i)},0);
}
Then you would get 3, 3, 3 as the output!
This is because the loop has already finished, including the final i++.
So now I think you can see the problem with your code. Upto calling jQuery.get you have access to the current value of icon_name, but once we are inside that asynchronous callback, the current value disappears and is replaced by the last value for it, because the loop already completed before any of the callbacks were executed.
Try something like this:
var svgIcons = {}
var props = ["arrow_left","arrow_right"];
this.getIcons = function() {
props.forEach(function(prop){
var url=PHP.plugin_url+'/includes/icons/'+prop+'.svg';
jQuery.get(url, function(data) {
svgIcons[prop]=data;
var fullyLoaded = false;
for(var i=0;i<props.length;i++) {
if(!svgIcons.hasOwnProperty(props[i])){
fullyLoaded = false;
break;
}
else fullyLoaded = true;
} // end for loop
if(fullyLoaded)
callMyFunctionWhereIUseSvgIconsData();
}); //end jQuery.get()
});//end forEach
}
this.getIcons()
This uses the forEach method, which is native to arrays (MDN reference). Inside the function passed to forEach, the first argument is always the current element of the array (which I named as prop). So there is no messy loop or i, and every executing function has access to its own prop property.
Then, inside the AJAX callback, I assign the current prop to the data received, and then loop through all the properties to check if the svgIcons object has received the properties. So fullyLoaded will only evaluate to true once all the callbacks have been executed and the global svgIcons has received all the properties and data. Hence, you can now call the function that uses the object.
Hope this helps, feel free to ask further or let me know if the console throws errors.
Any ajax calls are async therefore it can be run while the ajax call is taking place. If you want to call something after all calls are done then try this:
var svgIcons = { "arrow_left": "", "arrow_right":"", } //json object with empty values
var executing = 0;
this.getIcons = function() {
for (var icon_name in svgIcons) {
//store that this call has started
exectuing = executing + 1;
if (svgIcons.hasOwnProperty(icon_name)) {
var url=PHP.plugin_url+'/includes/icons/'+icon_name+'.svg';
console.log('this will run as you were expecting');
//this ajax call is then started and moves to next iteration
jQuery.get(url, function(data) {
//This is run after the ajax call has returned a response, not in the order of the code
svgIcons[icon_name]=data;
console.log('iterating');
console.log(svgIcons[icon_name]); //outputs svg
//if you want to call a function after evey call is comeplete then ignore the 'executing' part and just call the function here.
//decrement value as this call has finished
executing = executing - 1;
//if all have finished then call the function we want
if(executing === 0){
executeAfter();
}
});
}
}
console.log('this should be after iteration');
console.log(svgIcons["arrow_left"]); //empty
}
this.executeAfter(){
//This will be exectued after all of you ajax calls are complete.
}
this.getIcons(); //called at object initialization
I have the following function:
function loginStudent() {
var advisorKEY = "<dtml-var expr="py_get_alias()">";
var studentKEY = "<dtml-var SID>";
var URL = "py_logging_sessionOpen?AdvisorKEY=" + advisorKEY + "&StudentKEY=" + studentKEY;
key = "";
$j.get(URL, function(data) {
key = data;
});
alert(key);
}
The py_loggin_sessionOpen is just a python script running on my server.
It returns a single string. I need the response of that script to determine the next action. The script returns the value perfectly, and I can easily check the value by putting an alert within the function(data) in get.
My main question is: how to get the key value to be changed outside the scope of function(data)?
I assumed because I defined it externally it would act as a global variable.
Moving it outside loginStudent() does not solve the problem either.
Any ideas?
$j.get() is going to be an asynchronous call. That means it fires, and the rest of the execution continues. Anything that relies on that call needs to be done in the callback, like so:
$j.get(URL, function(data) {
key = data;
alert(key);
} );
If everything else is good, you'll see the value you expect.
The problem with your code is that $j.get executes asynchronously. That's the reason you pass a callback to it.
If you wish to write asynchronous code synchronously then you should read this answer: https://stackoverflow.com/a/14809354/783743
Edit: It seems that you have created a global variable called key by not declaring it with var. Hence it should be visible in other functions as long as they are called after the callback.
Would you care to provide us these other functions?
I have a simple function to count the number of rows in a database and return the result. However I am getting the result returned as undefined, even though if I console.log the result it is coming up correctly from within the function. Here is the gist of what I am doing although I have stripped out a lot of code to simplify it.
$('#roomsList').append(getCount(currentRow.roomtype));
function getCount(roomtype) {
var query = "SELECT COUNT(*) FROM fixturesfittings WHERE roomtype = ?;"
localDatabase.transaction(function (trxn) {
trxn.executeSql(query, [propertyid,roomtype],function (transaction, results) {
return results.rows.item(0)["COUNT(*)"];
},errorHandler);
});
}
Can anyone help me?
The problem is localDataBase.transaction and exequteSql are asynchronous functions. They won't have their answer right away, which is why you pass the functions into them. Once they gets an answer, they calls your function, known as a callback. This will happen at a later point in the execution cycle, after getCount is long gone.
So getCount calls localDatabase.transaction, which gets to work but doesn't have anything immediately available, so getCount finishes before the data is available, and so getCount is returning undefined (the default return value in JavaScript functions).
You will probably need to rework your code to something like this:
getCount(function(count) {
$('#roomsList').append(count);
});
function getCount(callback) {
var query = '...';
localDatabase.transaction(function(trxn) {
trxn.exequteSql(query, ... function(transaction, results) {
callback(results);
});
}
}
This is a very common pattern JavaScript, and has lots of pitfalls and oddities to it. It takes some getting used to.
There are two equally serious problems with what you're doing:
Your outer function doesn't return anything. The only return statement in your code is returning from the inner function being passed to localDatabase.transaction.
Something like this:
function myFunction
inner_function = function () {
return 3; # Return from inner function, *not* myFunction
}
}
Even if you were attempting to communicate a value out of your inner function to the outer function and then returning from the outer function, it still wouldn't work; the inner function is being invoked asynchronously. That inner return results... is being invoked at some point in the future, long after getCount has finished executing and returned undefined. You need to pass in some form of callback into getCount; the callback will be invoked later and passed the count. Think of it this way:
function myFunc(callback) {
function inner_function = function(callback) {
// Invoke our callback sometime in the future
setTimeout(1000, callback);
}
// Inner function does some processing at some future time;
// give it a callback to invoke when its processing is done
inner_function(callback);
}
I have a problem with a "static" function in javascrip (nodejs server).
User.create = function(data, _callback){
var node = db.createNode(data);
var _user = new User(node);
_user.save(function(err){
if(err) return callback(err, null);
_user.index(function(err){
if(err) return callback(err, null);
callback(null, _user);
})
})
};
If I call this function twice the _user variable in the internal callback function takes the new value, it seems it overrides the function var instead of allocate a new one.
I need calling this function to allocate a new variable, so it waits save and index functions to complete without changing _user variable.
JavaScript variables are indeed function scoped, so there wouldn't be any explanation for var _user not defining a new variable on subsequent runs.
Looking at the code, I would be more suspicious of what's happening in your User constructor - perhaps it contains some scoping or other logical issues resulting in identical users being created on subsequent calls. Similar "suspects" would be the data parameter getting passed in, as well as db.createNode(). Only suggesting these areas, because it's more likely that there's a programmatic issue at play, rather than JavaScript not following the rules :)
Also, I noticed that your User.create function accepts a parameter called _callback, but later on is invoking callback. I don't know if that's a typo in your example, or if you're accidentally invoking a callback from a higher scope not shown in the example, but that could produce weird behavior.