2012-05-19 65 views
0

我不得不采取以下函数来评估密码是否在用户的密码历史记录中。下面是代码:Node.js For循环内部异步回调

isPasswordInPwdHistory : function(redisClient, userId, encPassword, cb) { 

     var key = 'user:' + userId + ':pwdhistory'; 
     redisClient.llen(key, function(err, reply) { 
      if (err) { 
       status.results = [ ]; 
       xutils.addStatusResultsItem(status.results, err, null, null, null); 
       cb(status); 
      } 
      else { 
       var numPwds = reply; 

       var match = false; 
       var funcError; 

       for (i=0; i <= numPwds - 1; i++) { 
        var error; 
        var pwdValue; 
        redisClient.lindex(key, i, function(err, reply) { 
         if (err) { 
          console.log('lindex err = ' + err); 
          error = err; 
         } 
         else { 
          console.log('lindex reply = ' + reply); 
          console.log('lindex encPassword = ' + encPassword); 
          console.log('lindex (encPassword === reply) = ' + (encPassword === reply)); 
          pwdValue = reply; 
         } 
        }); 

        console.log('for-loop error = ' + error); 
        console.log('for-loop pwdValue = ' + pwdValue); 
        console.log('for-loop (encPassword === pwdValue) = ' + (encPassword === pwdValue)); 

        if (error) { 
         funcError = error; 
         break; 
        } 
        else if (encPassword === pwdValue) { 
         console.log('passwords match'); 
         match = true; 
         break; 
        } 
       } 

       console.log('funcError = ' + funcError); 
       console.log('match = ' + match); 

       if (funcError) { 
        status.results = [ ]; 
        xutils.addStatusResultsItem(status.results, err, null, null, null); 
        cb(status); 
       } 
       else 
        cb(match); 
      } 
     }); 
    } 

这里是控制台输出:

for-loop error = undefined 
for-loop pwdValue = undefined 
for-loop (encPassword === pwdValue) = false 
funcError = undefined 
match = false 
isPasswordInPwdHistory = false 
lindex reply = 5f4f68ed57af9cb064217e7c28124d9b 
lindex encPassword = 5f4f68ed57af9cb064217e7c28124d9b 
lindex (encPassword === reply) = true 

一旦我离开redisClient.lindex的范围()调用我失去了价值。我如何在for循环中将这些值传递给评估?

UPDATE

我重构一下代码索引i发出的redisClient.lindex()回调时来处理新的密码(encPassword)与现有密码的匹配。

isPasswordInPwdHistory : function(redisClient, userId, encPassword, cb) { 

     var status = new Object(); 
     var key = 'user:' + userId + ':pwdhistory'; 

     redisClient.llen(key, function(err, reply) { 
      if (err) { 
       status.results = [ ]; 
       xutils.addStatusResultsItem(status.results, err, null, null, null); 
       cb(status); 
      } 
      else { 
       var numPwds = reply; 
       var loopCt = 0; 

       for (i=0; i <= numPwds - 1; i++) { 
        loopCt++; 
        redisClient.lindex(key, i, function(err, reply) { 
         if (err) { 
          status.results = [ ]; 
          xutils.addStatusResultsItem(status.results, err, null, null, null); 
          cb(status); 
         } 
         else if (encPassword === reply) { 
          status.results = [ ]; 
          xutils.addStatusResultsItem(status.results, null, 0, null, true); 
          cb(status); 
         } 
         else if (loopCt === numPwds && encPassword !== reply) { 
          status.results = [ ]; 
          xutils.addStatusResultsItem(status.results, null, 0, null, false); 
          cb(status); 
         } 
        }); 
       } 
      } 
     }); 
    } 

不幸的是,调用者看到状态===未定义,即使encPassword ===回复为真,我发出cb(状态)。如何在发出cb(状态)后退出for-loop?

回答

1

看起来你在逻辑中混合了同步思维和异步思维。您的for循环将尽快向Redis发出请求,而无需等Redis响应(这是异步IO的性质)。因此,在您的代码运行console.log('funcError = ' + funcError);console.log('match = ' + match);时,Redis可能还没有响应循环中的第一个值i(与您在输出中找到的内容相匹配)。

我可能会考虑像async这样的库来帮助完成此任务;特别是whilst looks like it might be a good fit。也许是这样的:

var numPwds = reply; 
... 
var match = false; 
var i = 0; 

async.whilst(
    // keep looping until we have a match or we're done iterating the list 
    function() { return match == false && i < numPwds; }, 

    // this is our function to actually check Redis 
    function (callback) { 
    redisClient.lindex(key, i, function(err, reply) { 
     if (err) { 
     console.log('lindex err = ' + err); 
     error = err; 
     } 
     else { 
     if (encPassword === reply) { match = true; } // this will cause `whilst` to stop. 
     } 
     i++; 
     callback(); 
    }); 
    }, 

    function (err) { 
    // this is called when our first function that calls 
    // return match == false && i < numPwds 
    // returns false, which will happen when we're out of list elements to check 
    // or we have a match. At this point, match being true or false 
    // let us know if we found the password. 
    } 
); 

最后要注意的,除非你需要支持显示了多次的“拿来密码”名单相同的密码,您可以通过使用一组保存自己很多的心痛或一个有序集合。

+0

布兰登,谢谢你的反馈!我一定会检查异步库。至于使用Redis列表数据类型:我只关心存储最后5个密码。在将新项目添加到此列表中并超过5个元素后,我将列表重新分配回5个元素。实际上,我不需要设置/排序集的额外内存开销。但是,你指出这点非常敏锐。最适合你的家伙! – Chris

+0

我在redisClient.lindex()下添加了一个回调函数。 – Chris

+0

克里斯,编辑(你应该把你的问题编辑)将无法正常工作,因为你仍然没有处理在Redis IO回调中找到/未找到的行为(也就是传递给'isPasswordInPwdHistory'的主回调) - 你在任何Redis回调之外的主要执行路径中处理它。你无法完成这项工作 - 它与丢失范围无关,而是执行代码的顺序。在Redis能够响应其结果之前,您正在调用'cb(match)' 。 –