2

データベース内のデータをサーバーサイド JavaScript (ノード) の「クラス」としてモデル化しようとしています。今のところ、必要に応じてプロトタイプ メソッドを使用して従来のコンストラクター パターンを使用し、コンストラクター関数を全体として公開しているだけですmodule.exports。これがサーバー側であるという事実は、質問の核心にとって重要ではありませんが、参考として提供すると思いました。

コードの問題領域は次のようになります。

User.prototype.populate = function() {  
    var that = this;    
    db.collection("Users").findOne({email : that.email}, function(err, doc){
        if(!err) {
            that.firstName  = doc.firstName;
            that.lastName   = doc.lastName;
            that.password   = doc.password;
        }
        console.log(that); //expected result
    });
   console.log(that); //maintains initial values
};

findOne()この関数を呼び出すたびに、オブジェクトへの変更が完了すると保持されません。新しい関数スコープを持つグローバル オブジェクトへのthis変更のスコープに気付いたので、その参照を として維持しましたthatconsole.log(that)無名関数内からの場合、データは期待どおりにそのプロパティに表示されます。ただし、that関数が終了したらログに記録すると、関数の開始時の状態が維持されます。

ここで何が起こっているのか、どうすればインスタンス変数を期待どおりに変更できますか?

4

1 に答える 1

2

「しかし、関数が終了したらそれをログに記録すると、...」

これにより、あなたはこのようなことをしていると思います...

var user = new User

user.populate();

console.log(user);

その場合、非同期コールバックが呼び出されるconsole.logずっと前に が実行されます。.findOne()

への応答に依存するコードはfindOne、コールバック内で呼び出す必要があります。


編集:あなたの更新は上記の例とは少し異なりますが、理由は同じです。

メソッドにコールバックを渡す理由は、非同期アクティビティfindOneを実行するためです。そうでない場合、コールバックの理由はありません。の呼び出しの直後に内部コードを配置するだけです。findOneconsole.log()

ただし、非同期であるため、後続のコードは実行を待機しません。そのため、コンソールにデータが取り込まれていないオブジェクトが表示されます。

それぞれにラベルを追加するconsole.log()と、順不同で実行されることがわかります。


var that = this;    
db.collection("Users").findOne({email : that.email}, function(err, doc){
    if(!err) {
        that.firstName  = doc.firstName;
        that.lastName   = doc.lastName;
        that.password   = doc.password;
    }
    console.log("inside the callback", that); // this happens Last!!!
});

console.log("outside the callback", that); // this happens First!!!

したがって、呼び出しの順序を観察するconsole.logと、空の呼び出しがコールバック内の呼び出しの前に発生していることが明らかになります。


編集:.populate()コールバック内で呼び出されるコールバックをメソッドに受信させることもでき.findOneます。

User.prototype.createNickName = function () {
    this.nickname = this.firstName.slice(0,3) + '_' + this.lastName.slice(0,3);
};

    // >>>------------------------------v----receive a function argument...
User.prototype.populate = function(callback_func) {  
    var that = this;    
    db.collection("Users").findOne({email : that.email}, function(err, doc){
        if(!err) {
            that.firstName  = doc.firstName;
            that.lastName   = doc.lastName;
            that.password   = doc.password;
        }

          // all users will have the "createNickName()" method invoked
        that.createNickName();

          // ...and invoke it in the callback.
        callback_func.call(that);

          // By using .call(), I'm setting the "this" value
          //    of callback_func to whatever is passed as the first argument
    });
};

   // this user wants to log the firstName property in all caps
var user1 = new User;

user1.populate(function() {
    console.log(this.firstName.toUpperCase());
});


   // this user wants to log the the whole name
var user2 = new User;

user2.populate(function() {
    console.log(this.firstName + ' ' + this.lastName);
});


   // this user wants to verify that the password is sufficiently secure
var user3 = new User;

user3.populate(function() {
    console.log(verify_password(this.password));
});
于 2012-04-14T01:28:25.813 に答える