35

我正在为 MongoDB 使用 Node.JS 驱动程序,并且我想执行一个同步查询,如下所示:

function getAThing()
{
    var db = new mongo.Db("mydatabase", server, {});

    db.open(function(err, db)
    {
        db.authenticate("myuser", "mypassword", function(err, success)
        {
            if (success)
            {
                db.collection("Things", function(err, collection)
                {
                    collection.findOne({ name : "bob"}, function(err, thing)
                    {                           
                        return thing;
                    });
                });
            }
        });
    });
}

问题是,db.open 是一个异步调用(它不会阻塞),所以 getAThing 返回“未定义”,我希望它返回查询结果。我确信我可以使用某种阻塞机制,但我想知道做这样的事情的正确方法。

4

3 回答 3

32

ES 6(节点 8+)

您可以使用异步/等待

await运算符暂停异步函数的执行,直到 Promise 被解决并返回值。

这样您的代码将以同步方式工作:

const query = MySchema.findOne({ name: /tester/gi });
const userData = await query.exec();
console.log(userData)



较旧的解决方案 - 2013 年 6 月 ;)

现在Mongo Sync可用,这是在 Node.js 中进行同步 MongoDB 查询的正确方法。

我也用这个。您可以编写如下同步方法:

var Server = require("mongo-sync").Server;
var server = new Server('127.0.0.1');
var result = server.db("testdb").getCollection("testCollection").find().toArray();
console.log(result);

注意:它依赖于节点光纤,并且在 Windows 8 上存在一些问题。

快乐编码:)

于 2013-06-07T10:51:53.870 回答
22

没有办法使这种同步而没有某种可怕的黑客攻击。正确的方法是getAThing接受一个回调函数作为参数,然后在该函数thing可用时调用该函数。

function getAThing(callback)
{
    var db = new mongo.Db("mydatabase", server, {});

    db.open(function(err, db)
    {
        db.authenticate("myuser", "mypassword", function(err, success)
        {
            if (success)
            {
                db.collection("Things", function(err, collection)
                {
                    collection.findOne({ name : "bob"}, function(err, thing)
                    {       
                        db.close();                    
                        callback(err, thing);
                    });
                });
            }
        });
    });
}

节点 7.6+ 更新

async/await现在在使用返回 Promise 的异步 API 时提供了一种同步风格的编码方式(就像本机 MongoDB 驱动程序一样)。

使用这种方法,上述方法可以写成:

async function getAThing() {
    let db = await mongodb.MongoClient.connect('mongodb://server/mydatabase');
    if (await db.authenticate("myuser", "mypassword")) {
        let thing = await db.collection("Things").findOne({ name: "bob" });
        await db.close();
        return thing;
    }
}

然后您可以从另一个async函数调用它作为let thing = await getAThing();.

但是,值得注意的是它MongoClient提供了一个连接池,因此您不应该在此方法中打开和关闭它。相反,MongoClient.connect在您的应用启动期间调用,然后将您的方法简化为:

async function getAThing() {
    return db.collection("Things").findOne({ name: "bob" });
}

请注意,我们不是await在方法内调用,而是直接返回由findOne.

于 2012-08-19T22:03:08.877 回答
3

虽然它不是严格同步的,但我反复采用并发现非常有用的一种模式是在异步函数上使用copromisify yield。对于 mongo,你可以重写上面的:

var query = co( function* () {

    var db = new mongo.Db("mydatabase", server, {});
    db = promisify.object( db );
    db = yield db.open();

    yield db.authenticate("myuser", "mypassword");

    var collection = yield db.collection("Things");
    return yield collection.findOne( { name : "bob"} );

});

query.then( result => {

} ).catch( err => {

} );

这表示:

  1. 您可以使用任何异步库编写类似“同步”的代码
  2. 回调会抛出错误,这意味着您不需要成功检查
  3. 您可以将结果作为承诺传递给任何其他代码
于 2016-05-20T13:56:06.853 回答