Closures & async node.js functions

ぐ巨炮叔叔 提交于 2019-12-11 08:22:35

问题


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!


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/20505082/closures-async-node-js-functions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!