如何同步请求?

时间:2013-05-06 14:50:55

标签: node.js

我使用的是nodejs + express + mongoose。假设我有两个模式和模型:“水果”和“蔬菜”。

假设我有以下内容:

var testlist = ["Tomato", "Carrot", "Orange"];
var convertedList = [];
// Assume res is the "response" object in express

我希望能够分别针对“fruits”和“vegetables”集合检查数组中的每个项目,并将它们插入转换后的列表中,其中Tomato,Carrot和Broccoli将替换为各自的文档。

下面我有一些我认为会有的伪代码,但不知道怎么做。

for(var i = 0; i < testlist.length; i++) {
var fruitfind = Fruit.find({"name":testlist[i]});
var vegfind = Vegetables.find({"name":testlist[i]});

// If fruit only
if(fruitfind) {
convertedList.push(fruitfindresults);
} 
// If vegetable only
else if(vegfind) {

convertedList.push(vegfindresults);
} 
// If identified as a fruit and a vegetable (assume tomato is a doc listed under both fruit and vegetable collections)
else if (fruitfind && vegfind) {
convertedList.push(vegfindresults);
}
}

// Converted List should now contain the appropriate docs found.
res.send(convertedList) // Always appears to return empty array... how to deal with waiting for all the callbacks to finish for the fruitfind and vegfinds?

最好的方法是什么?或者这甚至可能吗?

2 个答案:

答案 0 :(得分:1)

假设每种水果/蔬菜中只有一种,并且你打算推两次收集的蔬菜两次。

var async = require("async"),
    testlist = ["Tomato", "Carrot", "Orange"];

async.map(testlist, function (plant, next) {
  async.parallel([function (done) {
    Fruit.findOne({"name": plant}, done);
  },
  function (done) {
    Vegetables.findOne({"name": plant}, done);
  }], function (err, plants) { // Edited: before it was (err, fruit, veggie) which is wrong
    next(err, plants);
  });
},
function (err, result) {
  var convertedList = [].concat(result);
  res.send(convertedList);
});

注意:实际上没有测试过代码,但它应该可以工作。 The async module非常适合管理像这样的回调。

<强>更新

要只获取每个水果一次,async.parallel回调只需要像这样重写:

function (err, plants) {
  next(err, plants[0] || plants[1]);
}

在.map回调中不再需要concat:

function (err, result) {
  res.send(result);
}

答案 1 :(得分:0)

find是一个异步函数,它向mongo数据库发出请求。这意味着两件事:

  1. 这些函数不会立即返回结果。 mongoose中的find函数遵循非常常见的异步模式。它接受&#34;回调&#34;它将使用结果或错误调用的函数。按节点约定,如果第一个参数不为null,则表示错误。

    // So typically you'd call find like this
    SomeModel.find({your: 'conditions'}, function (err, results) { 
      if (err) {
        // some error has occurred which you must handle
      } else {
        res.send(results);
      }
    })
    // note that if code existed on the lines following the find, it would
    // be executed *before* the find completed.
    
  2. 由于每个查询都会向数据库发送另一个请求,因此您通常希望限制该数量。在这种情况下,您可以使用mongo&#39; s $in一次查找所有名称,而不是按名称查找每个水果/蔬菜。

  3. 考虑到这两点,您的代码可能如下所示:

    // here *first* we're finding fruits
    Fruit.find({name: {$in: testlist}}, function (err, fruits) {
    
      // when the fruit request calls back with results, we find vegetables
      Vegetable.find({name: {$in: testlist}}, function (err, vegetables) {
    
        // finally concat and send the results
        res.send(fruits.concat(vegetables));
      });
    });
    

    要使两个请求并行发生,需要做更多的工作。您可以使用像async这样的库,或者自己写一些东西:

    var fruits
      , vegetables
      , done = function () {
          if (fruits && vegetables) {
            res.send(fruits.concat(vegetables));
          }
        }
    
    Fruit.find({name: {$in: testlist}}, function (err, docs) {
      fruits = docs;
      done();
    });
    
    Vegetable.find({name: {$in: testlist}}, function (err, docs) {
      vegetables = docs;
      done();
    });
    

    请注意,这里的两个示例都只是简单地结果并发送它们,因为它不清楚您希望如何处理结果。这意味着,如果番茄,例如,在两个列表中,它将在结果中出现两次,包括蔬菜和水果文件。

    您还需要处理从猫鼬回来的任何错误。

    修改:名称独特的文档

    根据您的评论,这是您可能只返回一个番茄文档(或其他水果和蔬菜记录)的一种方式

    // after retrieving fruits and vegetables, create a map which will
    // serve to weed out docs with duplicate names
    var map = {};
    
    fruits.forEach(function (fruit) {
      map[fruit.name] = fruit;
    });
    
    vegetables.forEach(function (vegetable) {
      map[vegetable.name] = vegetable;
    });
    
    var results = [];
    
    // this would sort by name
    Object.keys(map).sort().forEach(function (key, i) {
      results[i] = map[key];
    });
    
    res.send(results);
    

    请注意,如果您需要排序和分页或以其他方式限制两个查询的结果,这种事情会变得更加复杂,如果您需要,您可能宁愿考虑将文档保留在同一个集合中。