【问题标题】:Node.js asynchronous coding difficultyNode.js 异步编码难度
【发布时间】:2015-11-06 03:18:21
【问题描述】:

我正在尝试从 MongoDB 获取多个文档并将所有数据发送到一个数组中,但我很难理解如何使用事件驱动的 Node.js 来完成。

问题是在dataArray.push(tempObject)被执行的时候,tempObject["data"] = tempDataArray还没有被执行。

我的代码如下所示:

app.post('/api/charts', function(req, res) {
  var names = req.body.names;
  var categories = req.body.categories;

  var dataArray = [];

  for (i = 0; i < names.length; i++) {
    var tempObject = {};
    tempObject["name"] = names[i];
    Company.find({ name : names[i] }, function(err, result) {
      if (err) {
        throw err;
      }

      var tempDataArray = [];

      for (k = 0; k < categories.length; k++) {
        var tempDataObject = {};
        tempDataObject["name"] = categories[k];
        tempDataObject["numbers"] = result[0]["data"][categories[k]]["numbers"];
        tempDataObject["dates"] = result[0]["data"][categories[k]]["dates"];

        tempDataArray.push(tempDataObject);

      }
      tempObject["data"] = tempDataArray;
    });

    dataArray.push(tempObject);
  }

  res.send(dataArray);
});

任何关于如何正确实现预期结果的建议将不胜感激。

【问题讨论】:

  • 可以在 Nodejs 中使用 Promise 对象吗?
  • 我在理解它的工作原理时遇到了类似的困难。看看this。简而言之,当您拥有数据时,您需要发布处理所有内容 - 即在您的回调中查找,打包和发送响应,正如@kyrylkov 所说。

标签: javascript node.js mongodb asynchronous


【解决方案1】:

使用这个库

https://github.com/caolan/async

使用此代码,您的代码将如下所示:

var async = require("async");
app.post('/api/charts', function(req, res) {
    var names = req.body.names;
    var categories = req.body.categories;

    var dataArray = [];

    async.forEach(names, function(name, callback){

        var tempObject = {};
        tempObject["name"] = name;
        Company.find({ name : name }, function(err, result) {
            if (err) {
                callback(err);
            } else {
                var tempDataArray = [];

                for (k = 0; k < categories.length; k++) {
                    var tempDataObject = {};
                    tempDataObject["name"] = categories[k];
                    tempDataObject["numbers"] = result[0]["data"][categories[k]]["numbers"];
                    tempDataObject["dates"] = result[0]["data"][categories[k]]["dates"];

                    tempDataArray.push(tempDataObject);

                }
                tempObject["data"] = tempDataArray;
                dataArray.push(tempObject);
                callback();
            }
        });
    }, function(err){
        if(err){
            res.send(err);
        } else {
            res.send(dataArray);
        }
    });

});

【讨论】:

  • 是的。在这种 forEach 情况下,所有名称都将并行执行,一旦全部完成,您将获得最后一个回调函数 (err),其中我们使用 res.send 响应。如果您不想并行执行或者您想根据名称数组维护结果顺序,那么您可以使用 forEachLimit
【解决方案2】:

Company.find() 方法将回调函数作为它的第二个参数。此回调将在从数据库中检索公司数据后调用。这意味着在调用 Company.find() 方法之后调用它之前,它可能在几毫秒到几百毫秒之间的任何时间。但是直接在Company.find()之后的代码不会延迟;它会立即被调用。所以回调延迟就是为什么dataArray.push(tempObject)总是在tempObject["data"] = tempDataArray之前被调用。

最重要的是,外部 for 循环将同步运行,并且在每次迭代中都会进行单独的 DB 调用。这并不理想,因此我们希望将此 for 循环放入回调中。所以我们可以这样做:

app.post('/api/charts', function(req, res) {
    var names = req.body.names;
    var categories = req.body.categories;

    // we just do one DB query where all the data we need is returned
    Company.find({ name : names }, function(err, result) {
        if (err) {
            throw err;
        }

        var dataArray = [];

        // we iteratre through each result in the callback, not outside it since
        // that would cause blocking due to synchronous operation
        for (i = 0; i < result.length; i++) {
            var tempObject = {};
            tempObject["name"] = result[i].name;

            var tempDataArray = [];

            for (k = 0; k < categories.length; k++) {
                var tempDataObject = {};
                tempDataObject["name"] = categories[k];
                tempDataObject["numbers"] = result[i]["data"][categories[k]]["numbers"];
                tempDataObject["dates"] = result[i]["data"][categories[k]]["dates"];

                tempDataArray.push(tempDataObject);     
            }

            tempObject["data"] = tempDataArray;
            dataArray.push(tempObject);
        }
        res.send(dataArray);
    });
});

有许多方法可以抽象 Nodes 的事件驱动性质,例如 Promises(可以在 ECMA Script 6 或 Promise 库中访问,例如 Bluebird、Async 等)。但以上是一种基本的回调方法,通常用于 Express 应用程序之类的应用中。

【讨论】:

    【解决方案3】:

    只需更改即可:

    tempObject["data"] = tempDataArray;
    });
    dataArray.push(tempObject);
    

    收件人:

    tempObject["data"] = tempDataArray;
    dataArray.push(tempObject);
    });
    

    【讨论】:

    • 这只是为了解决问题。 res.send(dataArray) 将在大多数(如果不是全部)回调被执行之前被调用。而且它没有解决正在使用的同步方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-10
    • 2017-02-04
    • 2015-10-15
    • 1970-01-01
    • 2013-05-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多