0

まず、これは私にとって未知の領域であることをお許しください...

以下を得ました:

app.get('/user/:user_id/followings', function(req, res) {
    var response = {}
      , userId = req.params.user_id
      , ids = req.param('id').split(',')
      , prefix = 'user'
      , suffix = 'follower_ids';

    for (var i=0; i<ids.length; i++) {
      var id = ids[i]
        , key = prefix+':'+ids[i];

      console.log('1: ' + key);
      checkMembership(userId, id, key, suffix, function(error, reply){
        response[key] = reply;
        console.log('2: ' + key + ': ' + reply);
      });
    }

    res.json(response);
  });

  function checkMembership(userId, id, key, suffix, callback) {
    var lookup = key+':'+suffix;

    client.sismember(lookup, userId, callback);
  }

次のパスの場合: /user/1/followings?id=1,2,3,4,1000

コンソールでこれを取得します:

1: user:1
1: user:2
1: user:3
1: user:4
1: user:1000
2: user:1000: 0
2: user:1000: 1
2: user:1000: 1
2: user:1000: 1
2: user:1000: 0

そしてこれはブラウザで: {}

何かが非同期で発生しているため、それが発生していると思います...修正方法がわかりません。

checkMembership 関数に記録されたキーが間違っています。コンソールは、次のように 2 番目のコールバックを出力する必要があります。

2: user:1: 0
2: user:2: 1
2: user:3: 1
2: user:4: 1
2: user:1000: 0

ヘルプ!

4

1 に答える 1

5

これは、checkMembership が非同期であるためです。したがって、checkMembership がまだ回答を待っている間に for ループが終了します。

例を単純化することから始めましょう。

for (i = 0; i < 3; i += 1) {     

  console.log('inside: ', i);  

  process.nextTick(function () {  
    console.log('outside: ', i);    
  });                           

};      

console.log('end');   

出力:

inside:  0
inside:  1
inside:  2  
end                              
outside:  2
outside:  2
outside:  2

したがって、最初の問題は、カウンターが既に 2 になっているときにコールバックが実行されることです。これは、クロージャーを作成することで修正できます。

for (i = 0; i < 3; i += 1) {

  console.log('inside: ', i);

  (function () {
    var j = i; 
    process.nextTick(function () {
      console.log('outside: ', j);
    });    
  })(); 

};    

console.log('end');                           

出力:

inside:  0 
inside:  1 
inside:  2  
end
outside:  0                         
outside:  1 
outside:  2 

これにより、 の問題が解決されますresponse[key] = reply;

この例の 2 番目の問題は、非同期関数が完了する前に end がログに記録されることです。あなたのコードでは、これはキーと返信の設定が完了するres.json前に呼び出されることを意味します。checkMembership

そのため、終了をログに記録する前に、すべてのコールバックがいつ完了したかを確認する必要があります。リストの大きさが分かっているので、コールバックの数もわかります。これは、コールバックが行われるたびにカウントできることを意味します。カウントが同じサイズの場合、すべてのコールバックが呼び出されたことがわかります。

コードでは次のようになります。

var size = 3;                               
var counter = 0;                            

var globalCallback = function () {    

  counter += 1;                             

  if (counter === size) {                   
    console.log('end');                     
  }                                         

}                                           

for (i = 0; i < size; i += 1) {             

  console.log('inside: ', i);               

  (function () {                            
    var j = i;                              
    process.nextTick(function () {          
      console.log('outside: ', j);          
      globalCallback();                     
    });                                     
  })();                                     

};  

最終的な出力:

inside:  0
inside:  1
inside:  2
outside:  0
outside:  1
outside:  2
end

非同期関数のリストを取得するたびにこのようなカウンターを作成する代わりに、制御フロー ライブラリを使用することをお勧めします。async.js のようなものが非常に人気があります。

私を助けてくれたのは、自分のコードをより機能的に捉えることでした。値のリストとして表示し、値ごとに関数を適用する必要があります。これが、async.js が underscore.js に少し似ている理由でもあります。

于 2012-12-27T20:39:20.057 に答える