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);
}
Related
I am new to next(), done() etc. and am struggling with propagating parameters between serial executions/chaining of possibly otherwise asynchronous functions.
I want to force serial execution of 2 functions so they can be called with something like either:
f1('#{arg1a}', '#{arg1b}').done(
f2('#{arg2a}', '#{arg2b}')
);
OR something like:
f1('#{arg1a}', '#{arg1b}', '#{arg2a}', '#{arg2b}').done(f2);
Where the arg values passed in are gleaned from query parameters using JSF.
Note that:
arg2a and arg2b are in my case completely unrelated to arg1a and arg1b, and the invocation of f2 does NOT depend in any way on what happens in f1, other than it must execute strictly afterwards, even if f1 is otherwise usually asynchronous.
I am not defining on-the-fly anonymous functions or such inside done() here (yet), I want to be able to call a library-defined function with some known params.
In this example, the functions would be something like:
function f1(arg1a, arg1b) {
//do something with arg1a, arg1b
return $.Deferred().resolve();
}
function f2(arg2a, arg2b) {
// Do something with arg2a and arg2b AFTER f1 has fully run.
}
OR something like:
function f1(arg1a, arg1b, arg2a, arg2b) {
//do something with arg1a, arg1b
// Somehow ensure f1 is finished then execute f2(arg2a, arg2b)
}
function f2(arg2a, arg2b) {
// Do something with arg2a and arg2b AFTER f1 has fully run.
}
Just using callback chaining did not work for the situation I am tackling. See also: How link to and target/open a p:tab within an p:accordionPanel within a p:tab within a p:tabview
An acceptable answer MUST permit me to have a pre-defined function f2 with pre-defined parameters
You need to pass parameters to .resolve(), then use .then()
function f1(arg1a, arg1b) {
return $.Deferred(function(dfd) {
//do something with arg1a, arg1b
// you can alternatively call `.resolve()` without passing parameters
// when you are finished doing something with `arg1a`, `arg1b`,
// which should call chained `.then()` where `f2` is called
dfd.resolve(arg1a, arg1b)
}).promise();
}
function f2(arg2a, arg2b) {
// Do something with arg2a and arg2b AFTER f1 has fully run.
}
f1(arg1, arg2)
.then(function() {
// call `f2` here
f2('#{arg2a}', '#{arg2b}');
})
// handle errors
.catch(function(err) { // alternatively use `.fail()`
console.log(err)
});
jsfiddle https://jsfiddle.net/wuy8pj8d/
You've almost got it right except you've forgotten to wrap the code you want to execute in the future (when done is eventually called) inside a function:
f1('#{arg1a}', '#{arg1b}').done(function(){
f2('#{arg2a}', '#{arg2b}')
});
This also works with regular callbacks. For example, say you've defined f1 to accept a callback instead of a promise, you'd then do:
f1('#{arg1a}', '#{arg1b}',function(){
f2('#{arg2a}', '#{arg2b}')
});
Nothing special here. There's no separate syntax for forcing callbacks to accept custom arguments, just wrap it in another function.
This also works for variables thanks to closures:
var a='#{arg1a}', b='#{arg1b}';
var c='#{arg2a}', d='#{arg2b}';
f1(a,b).done(function(){
f2(c,d)
});
The variables c and d will be accessible within done().
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
please explain to me how this works. I'm new to nodejs and the way they set up their code is very complicated to me to properly understand..
I split the code that I don't understand into little snippets. You can find the whole code below.
callback(function() {
results.push(Array.prototype.slice.call(arguments));
next();
});
I don't understand the above snippet. It seems like this anonymous function becomes the next argument of the series anon function? Will the code in the anon function arguments execute?
function(next) { async(1, next); }
Which will execute first? The async function execution in the series functions or the next parameter execution?
Here I attached the full code:
function series(callbacks, last) {
var results = [];
function next() {
var callback = callbacks.shift();
if(callback) {
callback(function() {
results.push(Array.prototype.slice.call(arguments));
next();
});
} else {
last(results);
}
}
next();
}
function async(arg, callback) {
var delay = Math.floor(Math.random() * 5 + 1) * 100;
console.log('async with \''+arg+'\', return in '+delay+' ms');
setTimeout(function() {
callback(arg*2);
}, delay);
}
function final(results) {
console.log('Done', results);
}
series([
function(next) { async(1, next); },
function(next) { async(2, next); },
function(next) { async(3, next); },
function(next) { async(4, next); },
function(next) { async(5, next); },
function(next) { async(6, next); }
], final);
First, know that in JavaScript, functions can be passed as parameters to other functions. This is very different than passing the value returned from another function. Take this simple example:
function A() {
alert("In A");
}
function B(fn) {
alert("In B");
fn();
}
B(A); // alerts "In B", then "In A".
B(A()); // alerts "In A", then "In B",
// (then errors because the return value from A is undefined,
// and thus cannot be invoked.)
So, to follow your code example from beginning to end, here's how it goes...
series is a function that takes an array of functions (callbacks) and one more function (last) as parameters. It gets invoked first.
Within series, a function named next is defined (not to be confused with the parameter to each of the callback functions named next). The function next is invoked.
Within next, a variable named callback is defined. It's value is one of functions from the callbacks array in turn. The function stored in the callback variable gets invoked with an anonymous function.
Within callback, the async function is invoked. The same anonymous function from step 3 is passed to async. It's now called next, but this has nothing much to do with the next function defined in series.
Within async, some calculations are done and eventually the anonymous function is invoked (via setTimeout). It's called callback within the async function.
Within the anonymous function, some values are pushed onto the results array, and next is invoked. This is the next function defined in series.
Repeat steps 3 through 6 until all the functions within callbacks have been invoked and then the function in the parameter last (final) is invoked.
Clear as mud, right?
The series function takes a lists of functions to execute. Each of this functions must take a single parameter that needs to be a function, a callback. series uses that callback to know that a function finished its async work.
Here's a step by step of what series does:
Take a list of functions and a single callback named last
Create a results array where we'll store the results of all those functions
Pick the first item in the list and remove from the list
If the item is not a function (the list is empty):
Call last with the results array
Stop. We're done
If the item is a function call it and pass it a new callback that lets us know when it's done
Once the function is done doing its async work it should call the callback with any number of parameters. Store those parameters in the results array
Go to 3
Basically, it's a recursive function that waits until a process is done between each step. The results will be that each function you pass it in its callbacks list will be called sequentially, each after the previous finished doing its work.
Don't be discouraged. Async code is hard even for seasoned programmers.
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.
In my app I have the following:
client.on('test', function(req, fn) {
var returnArr = [];
redis.hkeys(req, function (err, replies) {
replies.forEach(function(reply, i) {
if (reply.indexOf('list.') > -1) {
redis.hgetall(reply.substring(5), function(err, r) {
returnArr.push({name:r['name'],index:i});
console.log(returnArr);
});
}
});
console.log(returnArr);
});
console.log(returnArr);
});
For some reason, the second and third logs contain a blank array even though the array is declared once at the beginnning of the event. Any ideas?
EDIT: Sorry, I changed the variable name when I posted it here without thinking. This happens when it's named anything.
Those redis calls are asynchronous. That's why you provide them with callbacks. The code won't work even if you fix the variable name for that reason.
To elaborate: the code in the callback to "hkeys" will be invoked when the data is available. The call will return immediately, however, so your array will have nothing in it at that point.
You cannot wrap asynchronous calls in a function and expect to return a value. It simply won't work.
Instead, the general pattern is to do exactly what the redis API (and virtually everything else in the node.js world; that's kind-of the whole point in fact): give your own function a callback argument to be invoked when appropriate. In your case, it'll be inside the "hgetall" callback that's the last one to be invoked. It should figure out that your results array has as many values in it as there are keys, and so it's time to call the callback passed in to your function.
(I should note that it's not clear what you're trying to do, given that the overall function appears to be a callback to something.)
Another approach would be to use some sort of "promise" pattern, though that's really just a restructuring of the same idea.
edit — the general pattern for an API with a callback would be something like this:
function yourAPI( param1, param2, callback ) {
// ...
some.asynchronousFunction( whatever, function( result ) {
callback( result );
}
}
Now in your case you're making multiple asynchronous service requests, and you'd need to figure out when it's time to invoke the callback. I think you'd probably want to iterate through the "replies" from the call to get the keys and extract the list of ones you want to fetch:
redis.hkeys(req, function (err, replies) {
var keys = [];
replies.forEach(function(reply, i) {
if (reply.indexOf('list.') > -1) {
keys.push( reply.substring(5) );
}
});
keys.forEach( function( key ) {
redis.hgetall(key, function(err, r) {
returnArr.push({name:r['name'],index:i});
if (returnArr.length === keys.length) {
// all values ready
callback( returnArr );
}
});
});
You cannot call your variable return
It is one of a few reserved words that you cannot use in your code as variables.
As Neal suggests don't use javascript reserved words for your variables, here is the list :
https://developer.mozilla.org/en/JavaScript/Reference/Reserved_Words
#Pointy answered this tersely already, but let me explain it a bit more clearly: Those nested functions are not being run in the order you think they are.
Node.js is non-blocking, and uses Javascript's implicit event loop to execute them when ready. Here's your code with line numbers:
/*01*/ client.on('test', function(req, fn) {
/*02*/ var returnArr = [];
/*03*/ redis.hkeys(req, function (err, replies) {
/*04*/ replies.forEach(function(reply, i) {
/*05*/ if (reply.indexOf('list.') > -1) {
/*06*/ redis.hgetall(reply.substring(5), function(err, r) {
/*07*/ returnArr.push({name:r['name'],index:i});
/*08*/ console.log(returnArr);
/*09*/ });
/*10*/ }
/*11*/ });
/*12*/ console.log(returnArr);
/*13*/ });
/*14*/ console.log(returnArr);
/*15*/ });
/*16*/ //Any other code you have after this.
So, what's the order of execution of this thing?
Line 1: Register the event handler for the 'test' event.
Line 16: Start running any other code to be run during this pass through the event loop
Line 2: A 'test' event has been received at some point by the event loop and is now being handled, so returnArr is initialized
Line 3: A non-blocking IO request is performed, and a callback function is registered to execute when the proper event is queued into the event loop.
Line 14-15: The last console.log is executed and this function is finished running, which should end the current event being processed.
Line 4: The request event returns and the callback is executed. The forEach method is one of the few blocking Node.js methods with a callback, so every callback is executed on every reply.
Line 5: The if statement is executed and either ends (goes to line 10) or enters the block (goes to line 6)
Line 6: A non-blocking IO request is performed, adding a new event to the event loop and a new callback to be run when the event comes back.
Line 9: Finishes the registration of the callback.
Line 10: Finishes the if statement
Line 11: Finishes the `forEach callbacks.
Line 12: Executes the second console.log request, which still has nothing in the returnArr
Line 7: One of the events returns and fires the event handler. The returnArr is given the new data.
Line 8: The first console.log is executed. Depending on which event this is, the length of the array will be different. Also the order of the array elements DOES NOT have to match the order of the replies listed in the replies array.
Essentially, you can look at the more deeply nested functions as executing after the entirety of the less-deeply nested functions (because that's what's happening, essentially), regardless of whether the method contains statements after nested non-blocking callback or not.
If this is confusing to you, you can write your callback code in a Continuation Passing Style so it's obvious that everything in the outer function is executed before the inner function, or you can use this nice async library to make your code look more imperative.
This, I think, answers your real question, rather than the one you've entered.