简体   繁体   中英

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!

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 .

Another method is to use async module , and this simplifies control flow which can improve readability as more async calls are introduced.

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);
}

becomes

function pollOID() {
    var log = function(channel, cb){
        session.get({ oid: channel.oid }, function (varbinds) {
           console.log("The " + channel.name + " is " + varbinds);
           cb(); // Moves onto next element in channels when cb is called
        });
    };
    var allDone = function(err, result) {
        // all tasks are complete
    };
    async.each(channels, log, allDone);
}

async.each will run in parallel so if it needs to be in order, use async.eachSeries instead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM