在nodejs(async)中寻找循环和回调中的正确模式

时间:2014-09-27 18:04:47

标签: javascript node.js asynchronous callback mongoose

我已经阅读了几篇关于此的帖子和文章,但我无法理解。

我的Mongoose模型中有一段代码,主要负责邀请人们参与项目。鉴于一些受邀者,我希望看看他们是否在数据库中,如果不是,我继续创建它们并更新被邀请者列表中的ID。

jslint抱怨循环并且我正在努力应对回调(当你有一个带有回调的数据库保存的循环时,正确的整体模式。显然我想要发生的是循环完全完成,任何新用户被添加到数据库中,ids是原始哈希(被邀请者)中的更新,然后回调发生。

ProjectSchema.methods.invite = function invite(invitees, callback) {
  var User = mongoose.model('User');
  var emails = _.pluck(invitees, 'email');
  // loop through invited hash to see if we already have those users in the db
  User.find({ 'email': { $in: emails}}, function (err, users) {
    for (var invited = 0; invited < invitees.length; invited++) {
      var found = false;
      // logic here to see if they are already users using the users param
      if (found) {
        // they are already in the db so do unrelated things.
      } else {
        // create new user
        var User = mongoose.model('User');
        var newUser = // set up new user here

        newUser.save(somecallback?);
        // update invitees list with id
      }
    }
    callback(err, invitees);
  });
};

1 个答案:

答案 0 :(得分:1)

这里有几个问题:

  1. 您在for循环中声明局部变量。这是不鼓励的,因为JavaScript具有功能范围,而不是块范围(可变提升)。

  2. 您需要某种方法来同步为保存到数据库的每个用户所做的异步更改,即“等待”循环完成。

  3. 对于1.,我建议你使用Array.map,即声明一个函数

    function processUser (invited) {
      // essentially the body of your for loop
    }
    

    然后拨打invitees.map(processUser)

    2.问题更难:我建议您使用提供异步支持的库,例如Q

    为此,让processUser函数返回已完成时已解析的Q promise,然后使用Q.all进行同步。就像这样:

    Q.all(invitees.map(processUser))
      .then(function (completions) {callback(null,invitees);},
            function (err) {callback(err,null);});
    

    结束:

    // import Q
    var Q = require('q');
    
    // ...
    
    ProjectSchema.methods.invite = function invite(invitees, callback) {
    
      var User = mongoose.model('User');
      var emails = _.pluck(invitees, 'email');
      // loop through invited hash to see if we already have those users in the db
    
      function processUser (invited) {
        var deferredCompletion = Q.defer();
    
        var found = false;
        // logic here to see if they are already users using the users param
        if (found) {
          // they are already in the db so do unrelated things.
          // ...
          deferredCompletion.resolve(true); // resolve with some dummy value
        } else {
          // create new user
          var newUser = // set up new user here
    
            newUser.save(function (err, user){
              if (err) {
                deferredCompletion.reject(err);
              } else {
                // update invitees list with id
                // ...
                deferredCompletion.resolve(true);
              }
            });
        }
        return deferredCompletion.promise;
      }
    
      Q.all(invitees.map(processUser))
        .then(function (completions) {
          callback(null,invitees);
        }, function (err) {
          callback(err,null);
        });
    };