异步Node.js循环中的变量范围

时间:2015-07-02 23:36:34

标签: node.js asynchronous promise sails.js q

我正在尝试在数组上运行一些数据库查询(使用sails.js),并在查询返回时执行某些操作。我认为最好的方法是使用for循环并解析promises async,一旦它们全部解决,继续。但是,只有我的数组中的最后一个promise才会解析,并且它会多次解析,因为在每个'User.findOne ...'然后函数中,索引是array.length-1。

我的问题:

  1. 异步循环中的变量范围如何工作?解释这个的最佳资源?
  2. 解决问题的最佳方法是什么?为什么?
  3. 我应该使用或不使用任何其他模式吗?我对promises和async js相当新,所以任何提示都会有所帮助!
  4. 我已检查的主要教程

    感谢您的帮助!

    我的简化代码:

    functionWhichReturnsPromise()
    .then(function(user){
      var promises = [];
      Q.try(function(){
        for (var index in array) {
          var fbid = array[index];// Get fbid from array
          promises.push(Q.defer().promise); // Add promise to promise array
    
          // Find userid from fbid; resolve respective promise when finished
          User.findOne({facebook_id: fbid}).then(function(userSeen){
            promises[index].resolve(userSeen.id);
            sails.log('resolved where id=' + userSeen.id); // correct
            sails.log('resolved where index=' + index); // PROBLEM: always last index
          });
        }
      }).then(function(){
    
        // For debugging purposes
        Q.delay(1000).then(function(){
          sails.log(promises[0]); // Unresolved
          sails.log(promises[1]); // Unresolved
          sails.log(promises[2]); // Only last promise in array is resolved
        });
    
        // When the userids have been extracted from above (promises fulfilled)...
        Q.all(promises).then(function(seenids){
          // Do stuff here (Doesn't get here)
        });
      });
    });
    

1 个答案:

答案 0 :(得分:2)

在Javascript中,变量的范围是函数而不是花括号。

因此,在下面的代码中,var index的范围不是for循环的花括号,范围实际上是for循环存在的函数。

Q.try(function(){
    for (var index in array) {
      var fbid = array[index];// Get fbid from array
      promises.push(Q.defer().promise); // Add promise to promise array

      // Find userid from fbid; resolve respective promise when finished
      User.findOne({facebook_id: fbid}).then(function(userSeen){
        promises[index].resolve(userSeen.id);
        sails.log('resolved where id=' + userSeen.id); // correct
        sails.log('resolved where index=' + index); // PROBLEM: always last index
      });
    }
  })

在for循环中,您调用异步函数,在您的情况下调用其mongodb调用(findOne)。 您应该始终假设这些异步函数可以运行任意数毫秒(取决于函数)。但总的来说,通常循环将在异步函数运行之前完成。即使在这些函数开始运行之前,你的for循环也会触发所有这些异步函数。问题是所有那些异常功能区域仍然​​指向该变量index。并且该变量对所有变量都是通用的,因为index在外部函数的范围内。

由于Javascript中的闭包,这是一个问题。要解决这个问题,我们需要使用更多的闭包。

关于闭包主题的资源很多,你可以谷歌。但请通过MDN's description of it

如果你在循环中的另一个函数中捕获index的值,那么你将会很高兴。

以下是我建议的解决方案。虽然我还没有测试过,但你明白了。

Q.try (function () {
    array.forEach( function(ele, idx, array) {
        (function(index) {
            var fbid = array[index]; // Get fbid from array
            promises.push(Q.defer().promise); // Add promise to promise array

            // Find userid from fbid; resolve respective promise when finished
            User.findOne({
                facebook_id : fbid
            }).then(function (userSeen) {
                promises[index].resolve(userSeen.id);
                sails.log('resolved where id=' + userSeen.id); // correct
                sails.log('resolved where index=' + index); // PROBLEM: always last index
            });
        })(idx);
    })
})

希望这有帮助。

另请注意: it is incorrect to use for...in for iterating through arrays