为什么async在循环中的node.js中给出错误

我在我的应用程序中有这部分代码。

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

当执行这给我错误。

 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) 

我不明白为什么它给这个错误?

得到卡从卡的mysql得到的结果

wrap函数执行callback函数。

getchannel从redis中返回数据。

你正在创build和传递到server.getchannel的函数是关于ivariables的closures(好了,覆盖范围内的所有内容,但这是我们所关心的)。 他们获得了对i持久的参考 ,而不是创build函数时的价值副本 。 这意味着当函数运行时,它使用i当前值,而不是创build函数时的值。 结果是所有这些函数将使用相同的值i ,这是循环结束时的值。 因为这超出了数组的末尾, resultscard[i]undefined ,所以试图从它读取owner属性失败。 (关于closures的更多信息: closures不复杂

所以你想要做的就是让这些函数closures一些i的价值的副本 。 通常的做法是有一个工厂函数来创build它们,并接受用作参数的值。 工厂函数创buildcallback函数,该函数closures参数,其值不会更改。

如果不仔细阅读,将其应用到代码中可能看起来像这样:

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

现在,我们在makeCallback创build的callbackmakeCallbackclosures了创build它的调用的index参数,没有其他更改。 我们通过i ,我们在那里。 它仍然是一个封闭的其他事情(因为makeCallback被定义在哪里),但它使用index ,所以它处理正确的条目。

这是javascript scope imo中最棘手的部分之一。

当你在一个循环内部,并且你正在创build一个基于循环索引的匿名函数时,你需要做一些类似于bindcurrying或者匿名的自我执行的函数 ,以确保你捕获了正确的值。

这个例子说明了这个概念:

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

这个的输出是:

 5 5 5 5 5 

预期? 不,你会期望0,1,2,3,4

这是因为基于外部范围的索引(在你创build的函数之外)的variables不会被复制,当函数被执行时(在一段时间之后,在循环已经完成之后),它被评估。

为了达到预期的效果,你可以做上面提到的任何事情。 这(可以说是最简单的)是一个自我执行的匿名函数

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

由于我们已经通过创build一个新函数来build立一个新的作用域,传递给我们感兴趣的variables(i),并且立即执行函数,所以这给了你所期望的0,1,2,3,4的输出所需的参数。 它采用(函数(a){})(a)的基本forms。

如果不知道这个块之外的代码的细节,你可能会这样做:

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