简体   繁体   English

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

[英]How do I send multiple queries from one endpoint with Express?

I am trying to query my database several times and construct an object which stores every response from my database in a field. 我试图多次查询数据库,并构造一个对象,该对象将数据库中的每个响应存储在一个字段中。 Here is my code: 这是我的代码:

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);
});

Since NodeJS/Express is asynchronous, this isn't working as I would like. 由于NodeJS / Express是异步的,因此无法正常运行。 How can I reconstruct this endpoint to make several database calls (from the same collection) and return an object containing it? 如何重构此端点以进行多个数据库调用(来自同一集合)并返回包含该端点的对象?

There's more than one way to do it: 有多种方法可以做到这一点:

Nested callbacks 嵌套回调

Without promises you could nest the callbacks: 没有承诺,您可以嵌套回调:

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);
        }))
    });
});

This would be the easiest way, but note that it is not efficient if those two requests could be done in parallel. 这将是最简单的方法,但请注意,如果可以并行执行这两个请求,则效率不高。

The async module async模块

You can use the async module: 您可以使用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);
    });
});

See: https://caolan.github.io/async/docs.html#parallel 参见: https : //caolan.github.io/async/docs.html#parallel

But this may still not be the most convenient method. 但这可能仍然不是最方便的方法。

ES2017 async / await ES2017 async / await

The most flexible way of doing that if you have 30 calls to make would be to: 如果您要打30次电话,最灵活的方法是:

  1. Use functions that return promises instead of functions that take callbacks 使用返回promise的函数,而不是使用回调的函数
  2. Use async/await if you can or at least generator based coroutines 如果可以或至少基于生成器的协程,请使用异步/等待
  3. Await on promises (or yield promises) when the logic needs to run in sequence 当逻辑需要按顺序运行时,等待承诺(或产量承诺)
  4. Use Promise.all() for anything that can be done in parallel 使用Promise.all()可以并行执行的任何操作

With async/await your code could look like this: 使用异步/等待,您的代码可能如下所示:

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

Or: 要么:

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

A big advantage of async / await is the error handling: 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
}

You can only use it inside of a function created with the async keyword. 您只能在使用async关键字创建的函数中使用它。 For more info, see: 有关更多信息,请参见:

For support in browsers, see: 有关浏览器的支持,请参阅:

For support in Node, see: 有关Node的支持,请参见:

In places where you don't have native support for async and await you can use Babel: 在您没有本地支持asyncawait ,可以使用Babel:

or with a slightly different syntax a generator based approach like in co or Bluebird coroutines: 或使用稍有不同的语法,使用基于生成器的方法(例如co或Bluebird协程):

See those answers for more info: 查看这些答案以获取更多信息:

You can do it with Promises 您可以用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({});
        });
});

Also you can use destructuring in callback functions like that: 您也可以在回调函数中使用解构,例如:

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

UPD: If you have a bunch of collection to request you can create an array of collections name, map it to promise requests and handle with Promise.all, for example 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 
    });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM