node.js/socket.io disappearing variable - javascript

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.

Related

Javascript returns result before all parameters evaluated?

I found the following example in ngResource documentation:
var cards = CreditCard.query(function() {
// GET: /user/123/card
// server returns: [ {id:456, number:'1234', name:'Smith'} ];
var card = cards[0];
// each item is an instance of CreditCard
expect(card instanceof CreditCard).toEqual(true);
card.name = "J. Smith";
// non GET methods are mapped onto the instances
card.$save();
// POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
// server returns: {id:456, number:'1234', name: 'J. Smith'};
// our custom method is mapped as well.
card.$charge({amount:9.99});
// POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
});
As I can understand, the second parameter of function query() is a function, which evaluated on success result of resource query. But simultaneously, this function takes the variable cards which is assigned from result of function query().
I can't understand, if this is normal to Javascript, since every async operation executes single thread?
Or special efforts were taken by creators of AngularJS in order to have function paramater executed after it's result returned?
How would I write my own function
function myfunction(argument, runbefore, runafter) {
runbefore();
POSTPONE runafter();
return Math.sin(argument);
}
which would execute 2nd parameter before itself and 3rd parameter -- after itself?
If I understand right, you are asking how it is possible for the callback function to be called after the return statement. One way that this is possible is through builtin functions that call another function at a later time. Take this code for example:
function doItLater(arg1, callbackFn) {
setTimeout(1000, callbackFn);
return arg1;
}
This will return the same argument that it was passed, and the callback function will be called later (about 1 second after the function has already returned). There are other ways a callback function can be delayed. For example, with an XMLHttpRequest, a callback function can be called after an HTTP response has been received. You can also connect to user events, so that a function will be called when the user does something specific.
If you want a little clarification on how things like setTimeout work in a single-threaded environment, I would suggest reading this article by John Resig.

JS asynchronous function in a loop

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

How do you pass a require('') object into the scope of the bind object?

I'm trying to use the javascript bind function to pass a file that I've required into the scope, and have it use that object's functionality to execute some code.
It's a recursive function, so to put it simply, I have an object:
var tts = require('./tts')
This object uses the Web API's Speech Synthesis functionality to turn the text that was passed in into speech.
So, I have a recursive function, say something, that should say the next thing, after the first one is done.
function saySomething(idx) {
tts('first thing',saysomething.bind(this,'next thing'))
}
Unfortunately, however, this does not work.
Can anyone tell me what I'm doing wrong?
My code:
tts code
recursive function code
P.S. I'm using browserify to compile the node-style code into browser-friendly code
Assuming tts() is an asynchronous function that calls its callback when it's done, you can call the next thing when that is done like this:
function saySomething(idx) {
tts('first thing', function() {
tts('next thing');
})
}
This would say first thing and then say next thing and then be done. While, at first glance, this appears to be recursive, it does not accumulate a stack frame with each call like a regular recursive call would because of the asynchronous nature of the callback. The first call to tts() has actually returned before the second one is called.
If what you really want to do is to iterate through an array of things to say, you can do that like this:
var phrases = ["one", "two", "three", "four"];
function sayPhrases(items) {
var index = 0;
function next() {
if (index < items.length) {
tts(items[index++], next);
}
}
next();
}
sayPhrases(phrases);

Passing variable in parent scope to callback function

This is more of a JavaScript Closure question than a Firebase question. In the following code, the Firebase callback isn't recognizing the variable myArr in the parent scope.
function show_fb() {
var myArr = [];
var firebase = new Firebase('https://scorching-fire-6816.firebaseio.com/');
firebase.on('child_added', function(snapshot) {
var newPost = snapshot.val();
myArr.push(newPost.user);
console.log(myArr); // works
});
console.log(myArr); // doesn't work. myArr in the firebase.on callback is
// not altering myArr
return myArr;
};
The callback is recognizing/modifying myArr perfectly fine. The problem is that when your "doesn't work"-labeled console.log(myArr) executes, the callback hasn't fired yet.
Let's change your code a bit:
var myArr = [];
function show_fb() {
var firebase = new Firebase('https://scorching-fire-6816.firebaseio.com/');
firebase.on('child_added', on_post_added); // steps 1-3
console.log(myArr); // step 4
return myArr; // step 5
};
function on_post_added(snapshot) { // step 6
var newPost = snapshot.val();
myArr.push(newPost.user); // step 7
console.log(myArr); // step 8
}
Now it might be a bit easier to see what's going on.
You register a listener for child_added that will call on_post_added for every post that is added to your Firebase
This will result in a call to the server, which may take a significant amount of time to return
Meanwhile your JavaScript code continues and...
Logs the array, which at this stage is still empty
And then thus returns an empty array
Now at some point the server returns the new value(s) and your callback is invoked
Which means we can add it to the array without problems
And logging it to the console shows the expected values
Handling asynchronous code/callbacks like this takes some getting used to, but is crucial to working with Firebase or any other AJAX-like or event driven technology. Putting the callback's code into a separate function sometimes makes it a bit easier to see what's going on.
In the case of Firebase it may also help to realize that the event is called child_added for a reason. It is called whenever a child is added to the Firebase, not just when you first register your callback. So minutes later when some other client adds a child, your callback will still fire, adding a new child to myArr. At that stage the code in steps 4 and 5 above will long have executed and will not execute again.
The solution is simple: put anything that you want to do after a child is added into your callback:
var myArr = [];
function show_fb() {
var firebase = new Firebase('https://scorching-fire-6816.firebaseio.com/');
firebase.on('child_added', on_post_added);
};
function on_post_added(snapshot) {
var newPost = snapshot.val();
myArr.push(newPost.user);
console.log(myArr);
// do whatever else you need to do for a new post
}
The child_added event is not immediately executed, therefore is not synchronous and you can't rely on it to have executed, before the log call at the end of your function.
The procedure is:
Define myArr
Instantiate Firebase
Assign event handler for child_added
Log the value of myArr
Return myArr - end of function
Now at some point after this, the child_added event is fired, which pushes to your array, but as you can see, your show_fb() function has already finished executing by this point.
If Firebase makes ajax call(it probably does), then callback function(snapshot){..} is called after return statement. So function show_fb always returns [].
for instance:
You execute this statement: var x=show_fb();
show_fb creates empty array
function creates ajax call
function returns myArr (it is empty at this moment)
variable x gets reference to myArr (array is still empty)
callback is called and inserts new value to x (x and myArr have same instance)

How to use callback function in JavaScript functions

I am very new to JavaScript and need to use callback function in my java script function. I don't know how to use a callback function. Below is my code:
function SelectedFeature() {
// Here is my code call_Method1();
call_Method2();
}
The problem in the above function is that, call_method2() starts executing before call_Method1() ends its execution. To solve this problem, someone told me to use a callback function. Now how can I use callback function in my SelectedFeature() function? Please explain by using code sample.
I'm making an asynchronous request in call_method1(). I need call_Method2() should be called after completing execution call_method1(). But in my case, call_method2() calls before call_method1() completes its execution. Now how can I fix this?
You have to refactor call_method1() to accept and execute a callback after it finished execution:
call_method1(call_method2);
and
function call_method1(callback) {
// ...
// do asynchronous stuff, when the response is processed, call
if(typeof callback === 'function') {
callback();
}
// ...
}
Functions are first class citizens, so by referring to them by their name, you can pass them around like any other value.
We could help better if you post the code for call_method1.
What are you using to do your asynchronous call? Did you code it yourself or are you using a library like JQuery?
You could simply put a bool to say "working" that you set to true as method 1 starts and back to false when it finishes. you could then have method2 wait while working is true.
The question has already been answered above by Felix. Inspired by his answer and an issue I am having in a current project, I wrote a little gist that has a class that adds up a little extra safety.
To sum up, you pass a callback function just as the way you pass a variable. Then the receiver will trigger it as a function.
myCoolFunction: function( data ) {
// Do some thing with response
}
$.get( '/some/cool/url', myCoolFunction );
In the above $.get calls back myCoolFunction with the parameter data once the data is fetched
What happens if myCoolFunciton is a variable. Well it depends on how the receiver handles the input.
Just to be careful, I have a CoffeeScript class ( and its JavaScript compilation ) that will do some safety checks.
It doesn't do any thing magic, checks if its a function and returns, if not returns an empty function so that it would reduce possibility of JS error. https://gist.github.com/ziyan-junaideen/8717925

Categories