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: user:1: 0
2: user:2: 1
2: user:3: 1
2: user:4: 1
2: user:1000: 0

帮助!

4

1 回答 1

5

你是对的,因为 checkMembership 是异步的。因此,您的 for 循环已完成,而 checkMembership 仍在等待答案。

让我们从简化您的示例开始:

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;

这个例子的第二个问题是 end 在 async 函数完成之前被记录。在您的代码中,这意味着在完成设置键+回复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 回答