【问题标题】:Node.js | MongoDB count(): Getting Count Before and After Inserting DataNode.js | MongoDB count():在插入数据之前和之后获取计数
【发布时间】:2017-01-21 16:37:02
【问题描述】:

我正在尝试获取数据库中的项目计数以确认数据插入成功。

  1. 插入前获取计数
  2. 插入
  3. 插入后获取计数
  4. Console.log 总结

注意:我知道这可以使用一些简单的函数来实现:

dbName.equal(insertSize, result.insertedCount)

但是,我是 javascript 新手,我想我遇到了实现异步回调的需要,所以我想弄清楚这一点。

插入函数

var insertMany = function() {

    // get initial count
    var count1 = getDbCount();

    // Insert new 'data'
    MongoClient.connect(url, function(err, db) {
        var col = db.collection(collectionName);
        col.insert(data, {w:1}, function(err,result) {});
        db.close();
    });

    /** This needs to be implemented through next/callback
        after the insert operation  **/
    var count2 = getDbCount(); 

    /** These final console logs should be executed after all
        other operations are completed **/    
    console.log('[Count] Start: ' + count1 + ' | End:' +count2);
    console.log('[Insert] Expected: ' + data.length + ' | Actual: ' + (count2 - count1));

};

获取数据库计数函数

var getDbCount = function() {
    MongoClient.connect(url, function(err, db) {
        if (err) console.log(err);

        var col = db.collection(collectionName);

        col.count({}, function(err, count) {
            if (err) console.log(err);
            db.close();
            console.log('docs count: ' + count);
            // This log works fine
        });

    });

    return count; // this is returning as undefined since this is
                  // executing before the count operation is completed
};

我收到错误,因为在所需操作完成之前发生退货。

感谢您的帮助。


[EDIT] 带有 Promise 的 getCount 函数

我已将承诺添加到 getCount 函数作为开始:

var getCount = function() {

    var dbCount = 0;

    var promise = new Promise(function(resolve, reject) {

        MongoClient.connect(url, function(err, db) {

            if (err) {
                console.log('Unable to connect to server', err);
            } else {
                console.log('Database connection established:' + dbName);
            }

            // Get the collection
            var col = db.collection(collectionName);

            col.count({}, function(err, count) {
                if (err) console.log(err);
                db.close();
                console.log('docs count: ' + count);
                resolve(null);
                dbCount = count;
            });

        });

    });

    promise.then(function() {
        return dbCount;
    });

};

console.log(getCount());

输出仍然是: 不明确的 建立数据库连接:testdb 文档数:500

then({return count}) 代码仍然在 promise {db.count()} 之前执行。在数据库操作完成之前返回 undefined。

【问题讨论】:

  • 如果你不展示你尝试过的东西,就很难说出你做错了什么。该错误应该带有一些关于错误发生在哪一行的信息。提供发生错误的代码块以及至少调用它的部分
  • 嗨 newBee,我已经把它拿出来了。我在通过回调实现方面没有取得太大进展,这就是我没有包含它的原因。如果我取得进展,我将更新并添加新代码。谢谢
  • @CarloP.LasMarias 你永远不应该选择setTimeout。有很多文章专门在后端解释了该功能的暴行。 Promise 是为处理此类情况而开发的模块,如果它不起作用,则问题出在代码中,需要解决。 Async.js 是另一个比 setTimeout 更好的选择。

标签: javascript node.js mongodb


【解决方案1】:

一般来说,您遇到的问题是您希望函数返回时该值已经存在。对于异步函数,情况通常并非如此。有大量关于异步处理的信息(不要与 Java 中的并行处理相混淆)。

我创建了一个适合您情况的示例:

https://jsfiddle.net/rh3gx76x/1/

var dummyCount = 5
getCount = function() {
    return new Promise(function(resolve, reject) {
      setTimeout(function() { // this would be your db call, counting your documents
          resolve(dummyCount); // dummy for number of documents found
      }, 100 * Math.random());
  });
};

insertMany = function() {
    return new Promise(function(resolve, reject) {
      setTimeout(function() { // this would be your db call, writing your documents
          dummyCount += 2;
          resolve();
      }, 100 * Math.random());
  });
};

runIt = function(callback) {
   var count1;
   getCount().then(function(count) {
        console.log("First callback with value ", count);
      count1 = count;
        insertMany().then(function() {
         getCount().then(function(count2){
            console.log("Second callback with value ", count2);
                callback(null, count1, count2);
         });
      })
   })
}

runIt(function(err, count1, count2) {
   console.log("count1: " + count1 + ", count2: " + count2);
});

最后一件事。您可能想查看“异步”包。提供大量辅助函数和控制流的东西,这对解决这些问题有很大帮助。

【讨论】:

  • setTimeout 是一种糟糕的做法。不建议专门在 I/O 任务中使用超时。您通过随机延迟代码来消耗时间。这会在开发复杂网站时消耗您的资源。
  • @Ritik Saxena 此处的超时只是替换原始海报制作的数据库调用,使示例更易于阅读。无法在您也没有提到的资源问题上关注您。将代码延迟到下一个事件循环滴答声并不少见。尽管您当然会始终使用“0”超时。
  • @newBee 通过使用 setTimeout 代替 db 调用以使示例更易于阅读,我希望您并不是说添加它是为了使示例更具可读性,没有任何其他目的。你是否?此外,reject 用于传递 promise 中的错误,这些错误在 catch 语句中处理。因此,您可能需要编辑注释 null = no error,因为此处的 null 表示代码没有任何内容可传递给 then
  • @newBee 我不完全明白你为什么使用超时。实际上,超时在后端是一种不常见的做法。在这种情况下,您的代码可能会起作用,但是在程序读取/写入数据库所花费的时间取决于数据库大小的情况下,您将设置一个随机的超时时间并希望最好的发生。因此,您的代码可能在这里可以工作,但这并不是一个始终遵循的好习惯。
  • @RitikSaxena 写了你的第一个回复:调用 runIt 函数时我没有回传 Promise。您误解了程序的流程。一般来说,使用承诺或回调方法是一个偏好问题。这里的超时只是为了模拟任何异步操作(例如磁盘 IO 或 DB 调用),因此提供了一个更通用的示例。 w.r.t 你的第二个问题:有些用例需要那些(或类似的函数,如 setImmediate)。例如。一次调用大量回调时。这可能会导致stack overflow 错误。
【解决方案2】:

在操作完成之前就发生返回的原因是nodejs的asynchronous设计,继承自javascript。来自Mongoose.connect() 的回调将在程序向前移动并遇到return 时进行操作。

您尚未提供使您进入错误声明的解决方法,因此我无法评论您在那里做错了什么。但是,考虑到您首先要完成您开始的工作,确保Mongoose.connect() 完成然后执行return 语句的最佳方法是使用JavaScript 的promise。使用 Promise 将使您的 Mongoose.connect() 执行,然后将控制权传递给 return 语句。

您的代码将类似于:

插入函数

var insertMany = function() {

// get initial count
var count1 = getDbCount();

// Insert new 'data'
var promise = new Promise(
    function(resolve,reject){
      MongoClient.connect(url, function(err, db) {
        var col = db.collection(collectionName);
        col.insert(data, {w:1}, function(err,result) {});
        db.close();
        resolve(null);
      });
    }
  );
  promise.then(function(val){    
    var count2 = getDbCount();

    /** These final console logs should be executed after all
    other operations are completed **/
    console.log('[Count] Start: ' + count1 + ' | End:' +count2);
    console.log('[Insert] Expected: ' + data.length + ' | Actual: ' + (count2 - count1));
});

同样,您可以将 promise 添加到 GetDB 函数。 执行 Promise 时的关键点是:

  1. then() 包含了 promise 执行后要同步执行的部分函数。被resolve调用,传入的参数在catch函数的回调中接收。
  2. catch() 包含在 Promise 中遇到错误时调用的函数部分。由reject调用,传入的参数在catch函数的回调中接收。

编辑:promise 的替代品是async.js,唯一的区别在于它是一个外部模块,与promise 不同。

【讨论】:

  • 请注意,此代码仍然会失败。 count2 将具有值“未定义”
  • @newBee 在我写完代码之后,我提到GetDB 也必须以这种方式执行。然后,count 就会有一个值返回给 count2。
  • 谢谢@RitikSaxena,我试过了,但仍然遇到同样的错误。即使在添加了 Promise 之后,then 语句仍然在 Promise 之前执行。我已经在修改后的 getCount 上面添加了。
  • @CarloP.LasMarias 您正在调用resolve,然后为dbCount 赋值。所以你的dbCount 永远不会被分配。试试这个:将null 替换为count 在解析中。在then函数的回调中,接收一个参数并赋值给dbCount
  • @RitikSaxena 我试过 resolve(count); dbCount = count;,也颠倒了顺序。结果还是一样,dbCount = undefined。
猜你喜欢
  • 2023-03-21
  • 2022-06-14
  • 1970-01-01
  • 1970-01-01
  • 2017-12-09
  • 1970-01-01
  • 2018-02-19
  • 2023-03-14
  • 1970-01-01
相关资源
最近更新 更多