【问题标题】:Saving multiple documents with Mongoose and doing something when last one is saved使用 Mongoose 保存多个文档并在保存最后一个文档时执行某些操作
【发布时间】:2020-11-07 11:11:00
【问题描述】:

我想使用 Mongoose 将 8 个对象保存到 MongoDB 数据库。保存最后一个文档时,我想报告(即发送事件)所有文档都已保存。

我现在这样做的方式非常混乱(尤其是对于我要保存的文档越来越多)。

这就是我现在拥有的方式(本示例仅适用于 4 人)。有没有更清洁的方法可以推荐?

person1.save(function(err, result){
    if (err) console.log(err);
    else{
        person2.save(function(err, result){
            if (err) console.log(err);
            else{
                person3.save(function(err, result){
                    if (err) console.log(err);
                    else{
                        person4.save(function(err, result){
                            if (err) console.log(err);
                            else{
                                done();
                            }
                        });
                    }
                });
            }
        });
    }
});

【问题讨论】:

    标签: javascript mongodb mongoose jasmine


    【解决方案1】:

    协调异步操作的有用库是async。在您的情况下,代码如下所示:

    var people = [ person1, person2, person3, person4, ... ];
    
    async.eachSeries(people, function(person, asyncdone) {
      person.save(asyncdone);
    }, function(err) {
      if (err) return console.log(err);
      done(); // or `done(err)` if you want the pass the error up
    });
    

    【讨论】:

    • 此解决方案需要额外的库,这会使应用程序更加繁重。仍然是很好的解决方案。我给 +
    • 异步库不是主要的补充。此外,由于这是服务器端,因此 async 的价值与它的占用空间小并没有真正对代码产生不利影响。
    • 对于这样的事情,你实际上想使用并行。
    • @DanSabin 最初的问题显示了一个顺序工作流,我使用async 复制了它,但我也会使用并行(或者可能是eachLimit
    • 我在哪里实现这个代码?我尝试将其粘贴到我的控制器上。它给了我person.save is not a function
    【解决方案2】:

    使用承诺和 Array.map()

    const userPromises = persons.map(user => {
      return new Promise((resolve, reject) => {
        person.save((error, result) => {
          if (error) {
            reject(error)
          }
          resolve(result);
        })
      })
    });
    
    Promise.all(userPromises).then((results) => {
      //yay!
      //results = [first, second, etc...]
    }, (error) => {
      //nay!
    })
    

    【讨论】:

      【解决方案3】:

      我建议有一个数组并通过迭代保存。将具有相同的性能,但代码会更干净。

      你可以拥有

      var Person = mongoose.model('Person');
      var people = [];
      
      people[0] = new Person({id: 'someid'});
      people[0].set('name', 'Mario');
      people[1] = new Person({id: 'someid'});
      people[1].set('name', 'Mario');
      people[2] = new Person({id: 'someid'});
      people[2].set('name', 'Mario');
      
      var errors = [];
      for(person in people){
       people[person].save(function(err, done){
        if(err) errors.push(err);
        if (person === people.length){ yourCallbackFunction(errors){
          if (errors.length!=0) console.log(errors);
          //yourcode here
         };
        }
       });
      }
      

      【讨论】:

      • 不过有一点不同:您的代码同时保存文档,而原始示例按顺序保存文档。
      • 在请求描述中作者没有提到这个顺序很重要,但我更新了我的 ans 以查看是否一切都做得很好。
      • @PawełSmołka 我想如果同时保存也没关系,我只想知道最后一个是什么时候保存的
      • 这会在最后一个人被保存并完成时触发,但不考虑是否所有人都真正完成了保存。只有 async 会正确计算所有已完成的保存。
      • 感谢您的提示,删除了else,现在应该没问题了。
      【解决方案4】:

      最好的方法是使用异步waterfall。 部分代码 sn-p 可能如下所示。请参考上面的链接。 它打破了异步性质并转换为一个接一个的进程(如果我没记错的话)。

       waterfall([
          function(callback){
       callback(null, 'one', 'two');
      },
         function(arg1, arg2, callback){
          callback(null, 'three');
        },
        function(arg1, callback){
       // arg1 now equals 'three' 
        callback(null, 'done');
        }
      ], function (err, result) {
        // result now equals 'done' 
      });
      

      【讨论】:

        【解决方案5】:

        我会使用 Underscore Each 来迭代一系列 People 模型。这将使您的代码更简洁,并有助于避免您在代码中遇到的"boomerang effect"

        来自documentation

        .each(list, iteratee, [context]) 别名:forEach

        遍历一个元素列表,将每个元素依次生成一个 iteratee 函数。如果传递了一个,则迭代对象绑定到上下文对象。每次调用 iteratee 时都使用三个参数:(元素、索引、列表)。如果 list 是 JavaScript 对象,iteratee 的参数将是 (value, key, list)。返回链表。

        例如:

        var people = [person1, person2];
        var length = people.length;
        var hasError = false;
        
        var doSomething =  function() {
            console.log("I'm done");
        }
        
        var doSomethingElse =  function() {
            console.log('There was an error');
        }
        
        _.each(people, function(person, i) {
        
            if (!hasError) {
        
                person.save(function(err, result) {
                    if (err) {
                        console.log(err);
                        hasError = true;
                    }
                );
        
                if (i === length) {
                    doSomething();
                }
        
            } else {
        
                // There was an error
                doSomethingElse();
            }
        
        });
        

        【讨论】:

        • 这将在 X 数量的保存操作 开始 时调用回调,但这并不一定意味着它们都已完成。另外,为什么不直接使用Array#forEach
        • 请参阅我关于操作完成的更新答案。
        • 为了回答您关于下划线的问题,与普通的 javascript 循环相比,我更喜欢 underscore.each 方法的便利性和简单性/可读性。
        • 我自己并没有看到_.each(array, fn)array.forEach(fn) 之间有很大的区别(实际上我发现第二个更易读)
        • 上面的帖子不保证 save() 对第一条评论中提到的所有情况都有效。
        猜你喜欢
        • 1970-01-01
        • 2021-05-15
        • 2021-03-04
        • 2014-04-07
        • 2012-05-03
        • 2017-10-06
        • 2016-02-15
        • 2020-04-05
        • 1970-01-01
        相关资源
        最近更新 更多