【问题标题】:MongoDB and Node js Asynchronous programmingMongoDB 和 Node js 异步编程
【发布时间】:2013-10-03 07:14:07
【问题描述】:

我正在尝试解决考试问题,因此无法按原样发布考试代码。因此,我进行了简化,以解决我不理解的核心概念。基本上,我不知道如何减慢节点的异步执行,以便我的 mongo 代码可以赶上它。代码如下:

MongoClient.connect('mongodb://localhost:27017/somedb', function(err, db) {
  if (err) throw err;
  var orphans  = [];
  for (var i; i < 100000; i++) {
    var query = { 'images' : i };
    db.collection('albums').findOne(query, function(err, doc_album) {
        if(err) throw err;
        if (doc_album === null) {
          orphans.push(i);
        }
    });
  }
  console.dir(orphans.length);
  return db.close();
});

所以我正在尝试创建一个不符合我的查询条件的图像数组。我最终的 orphans.length 值为 0,因为 Node 不等待回调完成。如何修改代码,使回调在我计算数组中不符合我的查询条件的图像数量之前完成执行?

提前感谢您的宝贵时间。

巴拉特

【问题讨论】:

  • 'images': {$in: [...]}

标签: javascript node.js mongodb asynchronous


【解决方案1】:

既然你知道它不会等待电话回来。您可以在回调函数中执行console.dir,这应该可以工作(虽然我还没有测试过)

    db.collection('albums').findOne(query, function(err, doc_album) {
        if(err) throw err;
        if (doc_album === null) {
          orphans.push(i);
        }
        console.dir(orphans.length);
    });

【讨论】:

    【解决方案2】:

    您不需要放慢任何速度。如果您只是尝试从相册集合中加载 100,000 张图像,您可以考虑使用async 框架。这将允许您分配任务,直到作业完成。

    另外,您可能不希望一个接一个地请求 100,000 条记录。相反,您可能希望对它们进行分页。

    【讨论】:

      【解决方案3】:

      我并不是建议这是在 Mongo 中处理此特定问题的最佳方法,但如果您需要等待数据库回复才能继续,那么只需使用回调开始下一个请求。

      这个一开始并不明显,但是可以参考函数本身内部的结果处理函数:

      var i = 0;
      var mycback = function(err, doc_album) {
          // ... process i-th result ...
          if (++i < 100000) {
              db.collections("album").findOne({'images': i}, mycback);
          } else {
              // request is complete, "return" result
              result_cback(null, res);
          }
      };
      db.collections('album').findOne({'images': 0}, mycback);
      

      这也意味着您的函数本身将是异步的(即希望使用 result_cback 参数来调用结果而不是使用 return)。

      编写一个调用异步函数的同步函数是不可能的。

      您不能“等待”Javascript 中的事件...您必须为结果设置一个处理程序然后终止。

      等待事件是在基于事件的处理中通过编写“嵌套事件循环”来完成的,这就是大多数 GUI 框架中处理消息框的方式。这是 Javascript 设计者不想给程序员的一种能力(虽然不太清楚为什么)。

      【讨论】:

        【解决方案4】:

        我假设您想要进行 100000 次并行数据库调用。为了在每个调用回调中“等待”10000 个调用完成,我们增加了完成调用计数器并在最后一个调用完成时调用主回调。请注意,这里非常常见的错误是将 for 循环变量用作回调内部的闭包。这不能按预期工作,因为首先调度的所有 10000 个处理程序,并且在第一次执行时循环变量具有相同的最大值。

        function getOrphans(cb) {
          MongoClient.connect('mongodb://localhost:27017/somedb', function(err, db) {
            if (err) cb(err);
            var orphans  = [];
        
            var numResponses = 0;
            var maxIndex = 100000
            for (var i = 0; i < maxIndex; i++) {
               // problem: by the time you get reply "i" would be 100000.
               // closure variable changed to function argument:  
               (function(index) {
                var query = { 'images' : index };
                db.collection('albums').findOne(query, function(err, doc_album) {
                  numResponses++;
                  if(err) cb(err);
                  if (doc_album === null) {
                    orphans.push(index);
                  }
                  if (numResponses == maxIndex) {
                    db.close();
                    cb(null, orphans);
                  }
                });
              })(i); // this is "immediately executed function
            }
          });
         }
        
        
         getOrphans(function(err, o) {
           if (err)
             return console.log('error:', err);
           console.log(o.length);
         });
        

        【讨论】:

        • 这行得通。谢谢。问题:所以这是因为您在上面的评论部分中显示的“立即执行的功能”?能否请您进一步详细说明。
        • 能否使用新的let 更轻松/更直观地解决这个问题,就像for (let i = 0; i &lt; maxIndex; i++) { ... } 一样?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-12-19
        • 1970-01-01
        • 2015-08-04
        • 1970-01-01
        • 2018-05-15
        • 1970-01-01
        • 2019-02-07
        相关资源
        最近更新 更多