简体   繁体   中英

why async in node.js in loop giving error

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

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