如何使用Express从一个端点发送多个查询?

时间:2017-04-10 14:55:01

标签: javascript node.js rest express

我正在尝试多次查询我的数据库并构建一个对象,该对象在一个字段中存储来自我的数据库的每个响应。这是我的代码:

router.post('/search', (req, res) => {
    var collection = db.get().collection('styles')
    var data = [];

    collection.distinct('make.name', (err, docs) => {
      data.push({'make': docs });
    });

    collection.distinct('model', (function (err, docs) {
        data.push({'model': docs });
    }))

    res.send(data);
});

由于NodeJS / Express是异步的,因此无法正常工作。如何重建此端点以进行多个数据库调用(来自同一个集合)并返回包含它的对象?

2 个答案:

答案 0 :(得分:5)

有多种方法可以做到:

嵌套回调

如果没有承诺,你可以嵌套回调:

router.post('/search', (req, res) => {
    var collection = db.get().collection('styles')
    var data = [];

    collection.distinct('make.name', (err, docs) => {
      if (err) {
        // ALWAYS HANDLE ERRORS!
      }
      data.push({'make': docs });
        collection.distinct('model', (function (err, docs) {
          if (err) {
            // ALWAYS HANDLE ERRORS!
          }
          data.push({'model': docs });
          res.send(data);
        }))
    });
});

这是最简单的方法,但请注意,如果这两个请求可以并行完成,效率不高。

async模块

您可以使用async模块:

router.post('/search', (req, res) => {
    var collection = db.get().collection('styles')
    var data = [];

    async.parallel({
      make: cb => collection.distinct('make.name', cb),
      model: cb => collection.distinct('model', cb),
    }, (err, responses) => {
      if (err) {
        // ALWAYS HANDLE ERRORS!
      }
      data.push({'make': responses.make });
      data.push({'model': responses.model });
      res.send(data);
    });
});

请参阅:https://caolan.github.io/async/docs.html#parallel

但这可能仍然不是最方便的方法。

ES2017 async / await

如果您有30次拨打电话,最灵活的方法是:

  1. 使用返回promises的函数而不是使用回调的函数
  2. 如果可以或至少使用基于生成器的协同程序
  3. ,请使用async / await
  4. 当逻辑需要按顺序运行时等待承诺(或产生承诺)
  5. 使用Promise.all()表示可以并行完成的任何事情
  6. 使用async / await,您的代码可能如下所示:

        // in sequence:    
        var make = await collection.distinct('make.name');
        var model = await collection.distinct('model');
        // use 'make' and 'model'
    

    或者:

        // in parallel:
        var array = await Promise.all([
          collection.distinct('make.name'),
          collection.distinct('model'),
        ]);
        // use array[0] and array[1]
    

    async / await的一大优势是错误处理:

    try {
      var x = await asyncFunc1();
      var array = await Promise.all([asyncFunc2(x), asyncFunc3(x)]);
      var y = asyncFunc4(array);
      console.log(await asyncFunc5(y));
    } catch (err) {
      // handle any error here
    }
    

    您只能在使用async关键字创建的函数中使用它。有关详细信息,请参阅:

    有关浏览器的支持,请参阅:

    有关Node的支持,请参阅:

    在您没有asyncawait原生支持的地方,您可以使用Babel:

    或略有不同的语法基于生成器的方法,如co或Bluebird协同程序:

    有关详细信息,请参阅这些答案:

答案 1 :(得分:2)

你可以用Promises

来做
router.post('/search', (req, res) => {
    var collection = db.get().collection('styles');
    // Create promise for "make.name" query
    let firstQuery = new Promise((resolve, reject) => {
        collection.distinct('make.name', (err, docs) => {
            if (!err) {
                resolve(docs);
            } else {
                reject(err);
            }
        });
    });
    // Create promise for "model" query
    let secondQuery = new Promise((resolve, reject) => {
        collection.distinct('model', (function (err, docs) {
            if (!err) {
                resolve(docs);
            } else {
                reject(err);
            }
        }))
    })
    // Run both queries at the same time and handle both resolve results or first reject
    Promise.all([firstQuery, secondQuery])
        .then((results) => {
            res.send({ "make.name": results[0], "model": results[1] });
        })
        .catch((err) => {
            // Catch error 
            res.send({});
        });
});

你也可以在回调函数中使用destructuring:

Promise.all([firstQuery, secondQuery])
    .then(([makeName, model]) => res.send({ "make.name": makeName, model }))

UPD:如果您有一堆要收集的集合,您可以创建一个集合名称数组,将其映射到承诺请求并使用Promise.all处理,例如

let collections = ["firstCollection", "secondCollection", "nCollection"];
let promises = collections.map((collectionName) => {
    return new Promise((resolve, reject) => {
        collection.distinct(collectionName, (err, docs) => {
            if (!err) {
                resolve(docs)
            } else {
                reject(err);
            }
        });
    })
});
Promise.all(promises)
    .then(results => {
        // Do what you want to do
    })
    .catch(error => {
        // or catch 
    });