deandob deandob - 6 months ago 28
Node.js Question

Closures & async node.js functions

All,

Trying to get my head around closures in a node.js context (async calls).

I have the following code:

timer = setInterval(pollOID, 1000);

function pollOID() {
for (channel in channels) {
session.get({ oid: channels[channel].oid }, function (varbinds) {
console.log("The " + channels[channel].name + " is " + varbinds);
});
}
}


The code polls a router for SNMP data each second using a loop in the setInterval callback to query the router for several SNMP entities. The session.get function has an async callback to process the results from the router.

The SNMP bits are working fine, my question is about how to persist the value of the loop variable channel inside the session async callback.

I get the following results:

The Download Attenuation is 7.5
The Download Attenuation is 361600
The Download Attenuation is 60


So the loop variable channel is changing for each call to session.get as the function is returning the correct value from the router. My problem is that channels[channel].name uses the current value of channel which by the time the callback has returned the loop has ended and channel is 2 (the third loop, which is the name string "download attenuation"). So I need to persist the value of channel inside the session.get callback to the value it was when the callback is called so the correct channels[channel].name is used in the session.get callback.

I know I have to use a closure for this but after trying a number of different approaches I can't get it working properly. Any clues to point me in the right direction? Thanks!

PSL PSL
Answer

You can create a simple closure to hold on to a local copy of channel.

 function pollOID() {
    for (channel in channels) {
      (function(channel){
        session.get({ oid: channels[channel].oid }, function (varbinds) {
               console.log("The " + channels[channel].name + " is " + varbinds);
        });
     })(channel);
  }

Or you can also use bind to pass in the argument, but the context will change inside the callback though.

for (channel in channels) {
    session.get({ oid: channels[channel].oid }, (function (channel, varbinds) {
           console.log("The " + channels[channel].name + " is " + varbinds);
    }).bind(this, channel));
}

And as you guessed your issue is due to accessing channel in the callback which is a shared variable and by the time the callback is invoked your for loop would have run through and channel will be holding the last key enumerated from channels.

Comments