node.js 使 mongoose.js 查询同步运行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17181248/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 14:49:28  来源:igfitidea点击:

Making mongoose.js queries run synchronously

node.jsmongodbmongoose

提问by AlexKinsella

I have two mongoose collections. The first stores a list of places, the second is visits to the places. My node code goes through and attempts to get the list of visits to each place and build a string that I output as JSON. The first query completes before the second ever starts - is there a way to make them run synchronously?

我有两个猫鼬系列。第一个存储地点列表,第二个是对地点的访问。我的节点代码通过并尝试获取每个地方的访问列表并构建一个我输出为 JSON 的字符串。第一个查询在第二个开始之前完成 - 有没有办法让它们同步运行?

回答by Harpreet Singh

If you are using node.js then u should use https://github.com/caolan/async

如果你使用 node.js 那么你应该使用https://github.com/caolan/async

when you have to fetch data from multiple collections you have to chain your queries multiple times.

当您必须从多个集合中获取数据时,您必须多次链接您的查询。

It will make your code complex and difficult to read and no modularity. Use async to create modularity using mongodb and node.js

它会使您的代码复杂且难以阅读,并且没有模块化。使用 async 使用 mongodb 和 node.js 创建模块化

Example Code from my project :

我的项目中的示例代码:

var async = require('async');

var createGlobalGroup = function(socket, data) {
    async.waterfall(
    [
    /**
     * this function is required to pass data recieved from client
     * @param  {Function} callback To pass data recieved from client
     */

    function(callback) {
        callback(null, socket, data);
    },
    /**
     * Step 1: Verify User
     */
    verifyUser,
    /**
     * Step 2: Check User Access Rights And Roles
     */
    checkUserAccessRightsAndRoles,
    /**
     * Step 3: Create Project
     */
    createNewGlobalGroup], function(err, result) {
        /**
         * function to be called when all functions in async array has been called
         */
        console.log('project created ....')
    });
}
verifyUser = function(socket, data, callback) {
//do your query
    /**
     * call next function in series
     * provide sufficient input to next function
     */
    callback(null, socket, data, {
        "isValidUser": true,
    });
}

checkUserAccessRightsAndRoles = function(socket, data, asyncObj, callback) {
    //do your query
    if(condition) {
        callback(null, socket, data, {
            roles: result,
            "isValidUser": asyncObj.isValidUser,
            "userId": asyncObj.userId,
        });
    } else {
    //no call back
    }
}

var createNewGlobalGroup = function(socket, data, asyncObj, callback) {
//wanna stop then no callback
}

回答by mr.freeze

There is no native synchronous api for mongodb/mongoose queries (and your wouldn't want one in practicality). As WiredPrarie mentions, you should chain the queries, with the second one starting after the first completes and running a callback. Here is an example:

没有用于 mongodb/mongoose 查询的本机同步 api(并且您在实践中不想要一个)。正如 WiredPrarie 所提到的,您应该链接查询,在第一个完成后开始第二个查询并运行回调。下面是一个例子:

function findVisits(placesQuery,callback){
    Places.find(placesQuery).exec(function(err,places){
        if (err || !places.length){
            console.log('there was a problem');
            callback(err, null);
        }else{
            var visitQuery = ... //however you want to filter places
            Visits.find(visitQuery).exec(function(err2,visits){
                if (err2 || !visits.length){
                    console.log('there was a problem');
                    callback(err2,null);
                }else{
                    callback(null, visits)
                }
            });
        }
    });
}

回答by vsenko

If you're using Node 8.x you can utilize async/await: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

如果您使用的是 Node 8.x,您可以使用 async/await:https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

await operator pauses execution of async function until the Promise is resolved and returns the value. This way your code will look more synchronous:

await 操作符暂停异步函数的执行,直到 Promise 被解析并返回值。这样你的代码看起来会更同步:

const query1 = MyModel.find({ name: /john/i }, null, { skip: 10 });
const result1 = await query1.exec();

const query2 = MyModel.find({ name: /john/i }, null, { skip: 100 });
const result2 = await query2.exec();

Queries will be executed in succession.

查询将被连续执行。

回答by joeytwiddle

New solution: async-await

新解决方案:async-await

Now that Javascript has async-await, you can use that, which will save a few lines, and flatten the code a bit:

现在 Javascript 具有 async-await,您可以使用它,这将节省几行代码,并使代码变平一点:

app.get('/notifications', async (req, res, next) => {
  try {
    const user = await Users.findOne({
      username: req.body.username,
      password: req.body.password,
    });
    if (!user) {
      res.json({success: false, message: "Username or password incorrect."});
      return;
    }

    const notifications = await Notifications.find({
      user: user._id
    });
    res.json({success: true, notifications});
  } catch (error) {
    //console.error(error);
    //res.json({success: false, error: error.message});
    next(error);
  }
});

Do not forget to catch the errors!

If the async function returns an error (in the form of a rejected promise), and express does not handle that error, this is called an unhandled rejection, and Node may decide to crash your process!

不要忘记捕捉错误!

如果 async 函数返回一个错误(以被拒绝的 promise 的形式),并且 express 不处理该错误,则称为 an unhandled rejection,并且 Node 可能会决定使您的进程崩溃!

Old solution: promises

旧解决方案:承诺

Mongoose supports promises these days, so you can .then()your queries. For example:

如今,Mongoose 支持 Promise,因此您可以.then()查询。例如:

app.get('/notifications', function (req, res, next) {
  Users.findOne({
    username: req.body.username,
    password: req.body.password,
  }).then(user => {
    if (!user) {
      res.json({success: false, message: "Username or password incorrect."});
      return;
    }

    return Notifications.find({
      user: user._id
    }).then(notifications => {
      res.json({success: true, notifications});
    });
  ).catch(error => {
    //console.error(error);
    //res.json({success: false, error: error.message});
    next(error);
  });
});

回答by Kohei Sugimura

To synchronize I used es6-promise.

为了同步,我使用了 es6-promise。

var Promise = require('es6-promise').Promise
  , mongoose = require('mongoose')
  , Schema = mongoose.Schema;

// define schemas and models.
var placeSchema = new Schema({
        name: { type: String },
        memo: { type: String }
    })
  , Places = mongoose.model('place', placeSchema)
  , visitSchema = new Schema({
        placeName: { type: String }, // foreign key for place.
        visitor: { type: String },
        comment: { type: String }
    })
  , Visits = mongoose.model('visit', visitSchema);

// query for visits by visitor and place.
function findVisitsWithPlace(visitor, place) {
    return new Promise(function (resolve, reject) {
        Visits.find({
            visitor: visitor,
            placeName: place.name
        }, function (error, visits) {
            if (error) {
                reject(error);
                return;
            }

            // build a result object you want.
            // ()
            resolve({
                place: place,
                visits: visits
            });
        });
    });
}

// functions for node route.
module.exports = {
    // - access to "GET /placevisits/?visitor=Visitor-1".
    get: function (request, response) {
        var visitor = request.query.visitor;

        // - to get the places...
        Places.find({}, function (error, places) {
            Promise.all(places.map(function (place) {
                // - run the child queries with parent object...
                return findVisitsWithPlace(visitor, place);
            })).then(function (placeAndVisits) {
                // - and get result.
                // placeAndVisits have still contain visits empty.
                // exclude them.
                var result = [];
                placeAndVisits.forEach(function (placeandvisit) {
                    if (placeandvisit.visits.length != 0) {
                        result.push(placeandvisit);
                    }
                });
                response.json(result);
            });
        });
    }
};

and I got JSON like following.

我得到了如下的 JSON。

[
    {
        "place": {
            "_id": "564e58a1dbed862155771d46",
            "name": "Place-A",
            "memo": "A memo for Place A."
        },
        "visits": [
            {
                "_id": "564e58cedbed862155771d49",
                "placeName": "Place-A",
                "visitor": "Visitor-1",
                "comment": "A comment for Place A by Visitor-1"
            },
            {
                "_id": "564e58dcdbed862155771d4a",
                "placeName": "Place-A",
                "visitor": "Visitor-1",
                "comment": "2nd visit. Again comment for Place A by Visitor-1"
            }
        ]
    },
    {
        "place": {
            "_id": "564e58afdbed862155771d47",
            "name": "Place-B",
            "memo": "A memo for Place B."
        },
        "visits": [
            {
                "_id": "564e58ebdbed862155771d4c",
                "placeName": "Place-B",
                "visitor": "Visitor-1",
                "comment": "A comment for Place B by Visitor-1"
            }
        ]
    }
]

回答by Proximo

Here's what I ended up doing tonight.

这就是我今晚最终要做的事情。

mongoose.createConnection("mongodb://localhost:27017/chemresearch")
.then(async db => {
    const collection = db.collection("chemical");
    const all = collection.find({});

    while(all.hasNext()) {
        let chemical = await all.next();
        await work(chemical);
    }
});

The workmethod is just returning a promise.

work方法只是返回一个承诺。

const work = (chemical) => new Promise((resolve, reject) => {
    const casNumber = chemical.casRegistryNumber;
    analysis(casNumber, (error) => {
        error ? reject(error) : resolve(casNumber);
    })
});

回答by Jeffrey A. Gochin

Here is an alternate method for making pseudo synchronous requests using MongooseJS. The idea here is to create a queue of queries that need to be executed. Then create a function that is called recursively until the queue is exhausted. Once the queue is exhausted, the recursion stops and a response sent back for the original request. I am using Express Routes, so all of this code is encapsulated in my route handler. In this case an HTTP POST.

这是使用 MongooseJS 发出伪同步请求的另一种方法。这里的想法是创建一个需要执行的查询队列。然后创建一个递归调用的函数,直到队列耗尽。一旦队列用完,递归就会停止,并为原始请求发回响应。我正在使用 Express Routes,因此所有这些代码都封装在我的路由处理程序中。在这种情况下是 HTTP POST。

var express = require('express');
var router = express.Router();

//POST /auth/create
router.post('/create', function(req, res) {
    var queue = [
        {"schema": require('..\models\people.js'), "query": {username: req.body.username}},
        {"schema": require('..\models\members.js'), "query": {username: req.body.username}}
    ],
    retData = []; 

    var curTask = 0.


    function recurse()
    {   
        if(curTask < queue.length){
                var task = queue[curTask];
                task.schema.findOne(task.query, function(err, data){
                retData.push(err || data);
                curTask++;
                recurse();
            })
        }else{
            res.json(retData);
        }

    }

    recurse();

});



module.exports = router;