【问题标题】:What is the right way to make a synchronous MongoDB query in Node.js?在 Node.js 中进行同步 MongoDB 查询的正确方法是什么?
【发布时间】:2012-08-15 08:17:53
【问题描述】:

我正在使用 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 返回“未定义”,我希望它返回查询结果。我确信我可以使用某种阻塞机制,但我想知道执行此类操作的正确方法。

【问题讨论】:

    标签: javascript node.js mongodb synchronous


    【解决方案1】:

    ES 6(节点 8+)

    您可以使用async/await

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

    注意:它依赖于node-fiber,并且在 Windows 8 上存在一些问题。

    编码愉快:)

    【讨论】:

    • 我用 mongo-sync 编写了一个 5 行脚本,但它失败了,尽管它几乎完美地匹配了他们的测试代码。它似乎有错误。
    • @jcollum :您能描述一下您遇到的确切问题吗?因为它对我有用,没有大问题。如果您确定它是模块中的错误,您可以在 Repo 上提出新问题
    • 我提交了一个错误。显然,您必须从 mongo-sync 库中的 node_modules 中删除光纤模块。看起来像是包装问题。
    • 如果它依赖于节点光纤,那么它不是同步的
    【解决方案2】:

    没有办法在没有某种可怕的黑客攻击的情况下实现这种同步。正确的方法是让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);
                        });
                    });
                }
            });
        });
    }
    

    Node 7.6+ 更新

    async/await 现在提供了一种在使用返回承诺的异步 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 返回的promise。

    【讨论】:

    • 感谢 Johnny 提供的解决方法!我希望有一个开箱即用的简单方法...即使编写一个简单的if_exists() 函数也令人沮丧...顺便说一句,如果有人知道更简单的方法或驱动程序的更新,请在此处发布。
    • 您可以随时使用异步库,以避免识别“厄运”github.com/caolan/async 这将使代码更具可读性和美观性。
    • @Logan 我不会称其为“解决方法”,这就是节点的设计方式。
    • 返回的thing 是一个'[object Promise]'。我们如何读取 Promise 对象中的数据? (没有.then 回调)
    • @user1063287 是的。有关更多示例,请参阅here
    【解决方案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. 您可以将结果作为承诺传递给任何其他代码

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多