why async in node.js in loop giving error - javascript

I have this part of code in my application.
card.getcard(command, function(toproceed,resultscard) {
console.log('entry other cards api result'+sys.inspect(resultscard));
if (resultscard.length==0) {
return proceed(false,{errno:'011','queueno' : request.queueno, message:'there is no access card for particular gib'});
}
for (var i=0; i<resultscard.length;i++) {
console.log('card owner'+resultscard[i].owner);
//checking that any users is in inside of gib
server.wrap(function(){
server.getchannel("channels."+request.gibid+'-I', function(err, channel) {
if (channel.users) {
var arr=channel.users.split(',');
if (functions.in_array(resultscard[i].owner, arr)) {
response.users.push(resultscard[i].owner);
}
}
});
if(i==resultscard.length-1) {
if (response.users.length<=0) {
//here need to send sorry event that no owner is online
request._command='sorry';
} else {
request._command='knock';
}
return proceed(true,response);
}
});
}
});
while executing this giving me error .
entry other cards api result[ { cardid: 16,
cardtype: 'A',
status: 'A',
refername: 'rahulgib',
refertype: 'G',
owner: 'rahul' },
{ cardid: 27,
cardtype: 'A',
status: 'A',
refername: 'rahulgib',
refertype: 'G',
owner: 'namita' } ]
card ownerrahul
card ownernamita
node.js:178
throw e; // process.nextTick error, or 'error' event on first tick
^
TypeError: Cannot read property 'owner' of undefined
at Object.callback (/home/myhome directory /redisyoungib/lib/yapi.js:271:50)
at RedisClient.return_reply (/usr/local/lib/node/.npm/redis/0.6.0/package/index.js:384:29)
at HiredisReplyParser.<anonymous> (/usr/local/lib/node/.npm/redis/0.6.0/package/index.js:78:14)
at HiredisReplyParser.emit (events.js:64:17)
at HiredisReplyParser.execute (/usr/local/lib/node/.npm/redis/0.6.0/package/lib/parser/hiredis.js:35:22)
at RedisClient.on_data (/usr/local/lib/node/.npm/redis/0.6.0/package/index.js:325:27)
at Socket.<anonymous> (/usr/local/lib/node/.npm/redis/0.6.0/package/index.js:90:14)
at Socket.emit (events.js:64:17)
at Socket._onReadable (net.js:673:14)
at IOWatcher.onReadable [as callback] (net.js:177:10)
I am not getting why it is giving this error ?
get card gives the result from mysql of card
wrap function executed the callback function .
getchannel return the data from redis .

The functions you're creating and passing into server.getchannel are closures over the i variable (well, over everything in scope, but it's i we're concerned with). They get an enduring reference to i, not a copy of its value as of when the function was created. That means when the function runs, it uses the current value of i, not the value as it was when the function was created. The upshot is that all of those functions will use the same value of i, which is the value as of the end of the loop. Since that's beyond the end of the array, resultscard[i] is undefined and so trying to read an owner property from it fails. (More about closures: Closures are not complicated)
So what you want to do is get those functions to close over something that's a copy of the value of i. The usual way to do that is to have a factory function that creates them and that accepts the value to use as an argument. The factory function creates the callback function, which closes over the argument, whose value doesn't change.
Without reading through it too carefully, applying that to your code probably looks something like this:
card.getcard(command, function(toproceed,resultscard) {
console.log('entry other cards api result'+sys.inspect(resultscard));
if (resultscard.length==0) {
return proceed(false,{errno:'011','queueno' : request.queueno, message:'there is no access card for particular gib'});
}
for (var i=0; i<resultscard.length;i++) {
console.log('card owner'+resultscard[i].owner);
//checking that any users is in inside of gib
server.wrap(function(){
server.getchannel("channels."+request.gibid+'-I', makeCallback(i));
// Call the factory function, passing in `i` -----^
if(i==resultscard.length-1) {
if (response.users.length<=0) {
//here need to send sorry event that no owner is online
request._command='sorry';
} else {
request._command='knock';
}
return proceed(true,response);
}
});
}
// The factory function
function makeCallback(index) {
return function(err, channel) {
if (channel.users) {
var arr=channel.users.split(',');
// Note we use `index` -- our argument -- not `i` below
if (functions.in_array(resultscard[index].owner, arr)) {
response.users.push(resultscard[index].owner);
}
}
};
}
});
Now the callback we create in makeCallback closes over the index argument for the call that created it, which nothing other changes. We pass i in, and there we are. It's still a closure over the other things (because of where makeCallback is defined), but it uses index with them so it handles the right entry.

This is one of the trickiest parts of javascript scope imo.
When you're inside a loop and you're creating anonymous functions based on the index from a loop, you need to do something like bind, currying, or anonymous self executing functions to make sure you are capturing the right value.
The concept is illustrated by this example:
var set = [];
// Store a list of functions in an array
for (var i = 0; i<5; i++) {
set.push(function(){
console.log(i);
});
}
// Pull the functions back out and execute them
for (var x = 0; x<5; x++) {
set[x]();
}
The output of this is:
5
5
5
5
5
Expected? No. You'd expect 0, 1, 2, 3, 4
This is because the variables based on the index for the outer scope (outside the function you've created) is not copied, its evaluated when the function is executed (some time later, after the loop is already through).
To get the desired affect, you can do any of the things I mentioned above. This (which is arguably the simplest) is a self executing anonymous function:
var set = [];
// Store a list of functions in an array
for (var i = 0; i<5; i++) {
(function(i){
set.push(function(){
console.log(i);
});
})(i);
}
// Pull the functions back out and execute them
for (var x = 0; x<5; x++) {
set[x]();
}
This gives you the desired output of 0, 1, 2, 3, 4 because we've established a new scope by creating a new function, passed in the variable we're interested in (i), and executed the function immediately with the desired parameters. It takes the basic form of (function(a){})(a).
Without knowing the details of your code beyond this block, you might do something like this:
card.getcard(command, function(toproceed,resultscard) {
console.log('entry other cards api result'+sys.inspect(resultscard));
if (resultscard.length==0) {
return proceed(false,{errno:'011','queueno' : request.queueno, message:'there is no access card for particular gib'});
}
for (var i=0; i<resultscard.length;i++) {
(function(resultscard, i){
console.log('card owner'+resultscard[i].owner);
//checking that any users is in inside of gib
server.wrap(function(){
server.getchannel("channels."+request.gibid+'-I', function(err, channel) {
if (channel.users) {
var arr=channel.users.split(',');
if (functions.in_array(resultscard[i].owner, arr)) {
response.users.push(resultscard[i].owner);
}
}
});
if(i==resultscard.length-1) {
if (response.users.length<=0) {
//here need to send sorry event that no owner is online
request._command='sorry';
} else {
request._command='knock';
}
return proceed(true,response);
}
});
})(resultscard, i);
}
});

Related

js get set with dynamic variables in node-opcua

Examples on node-opcua # https://github.com/node-opcua/node-opcua say that I need to rewrite code for every variable added to the OPC server, this is achieved calling 'addressSpace.addVariable()'... But if I have 1000 variables it could be an hard task... and eventually each custom user want a code rewrite, it could be tedious... so I'm trying to do it dynamically.
The opc read 'tags' from another custom server (not OPC).
With this 'tags' the opc server needs to add them to node 'device'.
When the OPC server node-opcua find a get or set of a variable coming from the net, it call the get or set of the correct variable:
for (var i = 0; i < tags.GetTags.length; i++)
{
variables[tags.GetTags[i].Tag] = {"value" : 0.0, "is_set" : false};
addressSpace.addVariable({
componentOf: device, // Parent node
browseName: tags.GetTags[i].Tag, // Variable name
dataType: "Double", // Type
value: {
get: function () {
//console.log(Object.getOwnPropertyNames(this));
return new opcua.Variant({dataType: opcua.DataType.Double, value: variables[this["browseName"]].value }); // WORKS
},
set: function (variant) {
//console.log(Object.getOwnPropertyNames(this));
variables[this["browseName"]].value = parseFloat(variant.value); // this["browseName"] = UNDEFINED!!!
variables[this["browseName"]].is_set = true;
return opcua.StatusCodes.Good;
}
}
});
console.log(tags.GetTags[i].Tag);
}
As I say I tried to use the 'this' in get and set functions with half luck, the get has a 'this.browseName' (the tag name) property that can be used to dynamic read my variables and it currently works.
The problem is with the set, in set 'this.browseName' and 'this.nodeId' don't exist! So it gives 'undefined' error. It also doesn't exist in variant variable.
Do you know a work-around to use dynamic variables with the above code? I need to have one for loop with one get and one set definitions for all variables (tags), that read and write a multi-property object or an array of objects, like 1 get and 1 set definitions that write the right variable in a n records array.
PS: I found on stack overflow this:
var foo = {
a: 5,
b: 6,
init: function() {
this.c = this.a + this.b;
return this;
}
}
But in my case node-opcua Variable doesn't has a 'this' working like the example. In the 'set' (like init): this.browseName (like a) and this.nodeId (like b) are not reachable.
Gotcha,
you need to cast get and set properties as functions like:
addressSpace.addVariable({
componentOf: device,
browseName: _vars[i].Tag,
dataType: "Double",
value: {
get: CastGetter(i),
set: CastSetter(i)
}
});
with
function CastGetter(index) {
return function() {
return new opcua.Variant({dataType: opcua.DataType.Double, value: opc_vars[index].Value });
};
}
function CastSetter(index) {
return function (variant) {
opc_vars[index].Value = parseFloat(variant.value);
opc_vars[index].IsSet = true;
return opcua.StatusCodes.Good;
};
}
you will use an index to get and set values in the array, casting function like this will provide index to be "hard coded" in those get and set properties.

javascript - issue with using .apply on functions

Okay so I have an object and I want to apply a callback function to all of the methods in the object. This is what I have tried so far:
var namespace = {
foo : 'bar',
foobar : function() { console.log('call from foobar!')},
someFunc : function() { console.log('call from someFunc!')},
someFunc2 : function() { console.log('call from someFunc2!')}
}
var logger = {
_callback : function () {
console.log('call from logger!',arguments);
}
}
for (var m in namespace) {
if ( namespace.hasOwnProperty(m) && (typeof namespace[m]=='function') ) {
logger[m] = namespace[m];
namespace[m] = function() {
logger._callback(arguments);
logger[m].apply(this, arguments);
}
}
}
namespace.foobar('foo');
namespace.someFunc('bar');
namespace.someFunc2('bar2');
This is what is getting logged to the console:
call from logger! [["foo"]]
call from someFunc2!
call from logger! [["bar"]]
call from someFunc2!
call from logger! [["bar2"]]
call from someFunc2!
As you can see, for some reason all 3 methods of namespace are outputting 'call from someFunc2! which is wrong. I'm not sure what the issue here is.. what am I doing wrong?
Try
for (var m in namespace) {
if ( namespace.hasOwnProperty(m) && (typeof namespace[m]=='function') ) {
logger[m] = namespace[m];
(function(index){
namespace[index] = function() {
logger._callback(arguments);
logger[index].apply(this, arguments);
};
})(m);
}
}
otherwise the namespace[m] = function(){} will use whatever m is last
There's just one "m". The code inside that function you create in the for loop references the "live" value of "m", not a value frozen at the point the function was created. The last value it takes on is name "someFunc2", so that's the one that's called.
Step by step:
You create the "namespace" and "logger" objects.
The loop runs. The variable "m" takes on the successive values of the properties in the "namespace" object, and creates a new function for each relevant property of that object.
At the end of the loop, "m" has the value "someFunc2".
You call one of the "namespace" functions. That'll be a call to one of the functions created in the loop. That function will in turn call the "_callback" function. And now the important key point: it references a property of the "logger" object using the value of "m". What is the value of "m"? It's "someFunc2".

Do I need a closure inside a DOM event callback?

I'm trying to build a jQuery.live like function.
Helper is a class that has the _liveEvent and _addEventListener methods. Helper._addEventListener is just a CrossBrowser version of W3C addEventListener.
Helper.prototype._liveEvent = function(type, evt, ofunc) {
var elHand = document;
type = type.toUpperCase();
this._addEventListener(elHand, evt, function(me) {
// Inside here I use the `type` variable.
// I don't know why but it works.
for (var el = me.srcElement; el.nodeName !== 'HTML';
el = el.parentNode)
{
if (el.nodeName === type || el.parentNode === null) {
break;
}
}
if (el && el.nodeName === type) {
ofunc.call(el, me);
}
});
};
I'm running the Helper._liveEvent function twice with different types, and it works just fine. I thought that since the type variable was set inside the _liveEvent context the _addEventListener callback could see only the last version of that variable. But it's not the case, it seems to be working fine.
My questions are:
Why the _addEventListener callback can see both versions of the type?
Does it mean my code is leaking memory?
UPDATE
This other example made me understand this better, but I'm not sure I understand it fully yet.
function foo(i) {
setTimeout(function() {
console.log(i);
}, 400);
}
// Prints 1, 2, 3
for (var i = 1; i < 4; i++) {
foo(i);
}
function bar() {
for (var i = 1; i < 4; i++) {
setTimeout(function() {
console.log(i);
}, 400);
}
}
// Prints 4, 4, 4
bar();
​
It's because a separate closure scope is created for every instance of the anonymous function passed to _addEventListener(), each having its own values of elHand and type.
It depends on what you mean by "leaking". Every closure prevents objects that it contains from GC'ing. A closure is GC'ed when there are no more objects (say, anonymous functions like yours) referencing it. In this sense, yes, you have a memory leak, as you have no way to remove the added listener (anonymous function) thereby making the associated scope object eligible for GC.
Effectively, you already are creating a closure. This is why:
for( var i=0; i<10; i++) {
elem.onclick = (function(id) {alert(id);})(i);
}
works - calling the anonymous function creates a new closure with id set to the current value of i. (Personally I like to call the argument the same thing as the variable I want to use, so I can think of it as "locking" the value of the variable for that function).
As far as memory leaks go, two calls is not going to cause a leak. If GC works the way I think it does, it removes any closures that have no pointers to them. In particular, when you leave a page, any memory associated with that page is freed.

Jquery assignment fails when inside a for loop

I have a jquery which works correctly when I do this:
var slide = [];
slide[1] =
{
hide: function() {
$("#slide-1").hide();
},
show: function() {
$("#slide-1").show(2000);
}
};
slide[1].show(); <<< works fine
But if I try it in a loop in fails:
for (var i=1; i <= totalSlides; i++) {
slide[i] =
{
hide: function() {
$("#slide-" + i).hide();
},
show: function() {
$("#slide-" + i).show(2000);
}
};
};
slide[1].show(); << unassigned
any idea?
Well, you're saying that it is "unassigned" but I'm guessing that the function is just not doing what you want.
This is a common issue. All the functions you're creating in the loop are referencing the same i variable. This means that when the function runs, it is getting the value of i where it was left after the loop finished.
You need to scope the variable that your functions reference in a new variable environment to retain the value from the loop. To do that, you need to invoke a function, and have that function reference the current i value.
Like this:
function generate_functions( j ) {
// v----- DO NOT place the opening brace on the next line, after the
return { // return statement, or your code will break!!!
hide: function() {
$("#slide-" + j).hide();
},
show: function() {
$("#slide-" + j).show(2000);
}
};
}
var slide = [];
for (var i=1; i <= totalSlides; i++) {
slide[i] = generate_functions( i );
};
slide[1].show(); // should work
I created a function called generate_functions(), and invoked it in each iteration, passing i as an argument.
You'll notice that generate_functions() received the value as the j parameter. You could call it i as well, but changing the name makes it a little clearer IMO.
So now your functions are referencing the local j. Because a new variable environment is created with each invocation of generate_functions(), the functions inside that you create will be referencing the j value of that specific variable environment.
So the generate_functions() returns the object that contains the functions that were created in each new variable environment, and that object is assigned to slide[i].
Is the $("slide-1" + i).show(2000) a typo, or the error?
Add var slide = []; above the for loop.

How to access this variable in an inline function?

Here is my dilemma.
I've got this section of code:
var list_of_numbers = new Array();
function AddToArray(func)
{
// Add to the *beginning* of the array
// essentially reversing the order
list_of_numbers.unshift(func);
}
function DisplayNumber(num)
{
document.write(num);
}
for(var i=0;i<5;++i)
{
AddToArray(function() { DisplayNumber(i); });
}
for(var i=0;i<5;++i)
{
list_of_numbers[i]();
}​
What is supposed to happen is that 5 inline functions will be added to the array - each taking a copy of i. However this does not happen.
Expected output:
43210
Actual output:
01234
You have two separate issues, both related to scope.
var list_of_numbers = new Array();
function AddToArray(func)
{
// Add to the *beginning* of the array
// essentially reversing the order
list_of_numbers.unshift(func);
}
function DisplayNumber(num)
{
document.write(num);
}
for(var i=0;i<5;++i)
{
(function(i)
{
AddToArray(function(){ DisplayNumber(i); });
})(i);
}
for(var j=0;j<5;++j)
{
list_of_numbers[j]();
}​
The anonymous function you're passing to AddToArray is bound to the variable i, not the current value. To address this, we create a new function, and pass in the current i.
JavaScript has function scope, so when you re-declare i in the second loop, you're still modifying the same variable. Thus, we rename it to j.
If only the first were an issue, you would get 55555, since all functions would use the same i, at that point 5. However, since you reuse i for the second index, i is set to the current loop index.

Categories

Resources