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

Posted

技术标签:

【中文标题】在nodejs(异步)中寻找循环和回调的正确模式【英文标题】:Looking for proper pattern in looping and callbacks in nodejs (async) 【发布时间】:2014-11-22 12:43:08 【问题描述】:

我已经阅读了几篇关于此的帖子和文章,但我不太明白。

我的 Mongoose 模型中有一段代码,它基本上负责邀请人们加入一个项目。给定一些受邀者,我会查看他们是否在数据库中,如果没有,我会继续创建它们并更新受邀者列表中的 id。

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

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】:

这里有几个问题:

    您在 for 循环中声明局部变量。不鼓励这样做,因为 javascript 具有函数作用域,而不是块作用域(变量提升)。

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

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

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

然后拨打invitees.map(processUser)

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

为此,请让processUser 函数返回一个在完成后解析的 Q 承诺,然后使用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);
    );
;

【讨论】:

或者,您可以使用async.mapasync.mapLimit。如果您要处理大量用户并且不想使数据库过载,则后者很有用。

以上是关于在nodejs(异步)中寻找循环和回调的正确模式的主要内容,如果未能解决你的问题,请参考以下文章

如何优雅的处理Nodejs中的异步回调

如何优雅的处理Nodejs中的异步回调

[nodejs基础]eventloop机制图解

如何使用 nodejs 将异步用于回调?

nodejs所用的概念(同步,异步,事件驱动,事件循环等)通俗解释

nodejs事件循环