0

パスワードがユーザーのパスワード履歴にあるかどうかを評価する関数に従う必要があります。コードは次のとおりです。

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ループで評価するためにこれらの値を渡すにはどうすればよいですか?

アップデート

redisClient.lindex()コールバックが発行されたときに、新しいパスワード(encPassword)とインデックスiの既存のパスワードの一致を処理するために、コードを少しリファクタリングしました。

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 === replyがtrueであり、cb(status)を発行しても、呼び出し元にはstatus===undefinedが表示されます。cb(status)を発行した後、forループを完全に終了するにはどうすればよいですか?

4

1 に答える 1

1

ロジックに同期的思考と非同期的思考が混在しているようです。ループは、 Redisforが応答するのを待たずに、Redisへのリクエストをできるだけ早く実行します(これは非同期IOの性質です)。そのため、コードが実行されるまでconsole.log('funcError = ' + funcError);console.log('match = ' + match);、Redisはループ内の最初の値i(出力で見つけたものと一致する)に対して応答すらしていない可能性があります。

私はおそらく、このタスクを支援するためにasyncのようなライブラリを調べます。特に、whilstそれがぴったりかもしれないように見えます。おそらく次のようなものです:

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

最後に、「使用済みパスワード」リストに同じパスワードが複数回表示されるのをサポートする必要がない限り、セットまたはソートされたセットを使用することで、多くの苦痛を軽減できます。

于 2012-05-19T18:15:56.770 に答える