在 Node.js 中进行同步 MongoDB 查询的正确方法是什么?

IT技术 javascript node.js mongodb synchronous
2021-02-25 05:04:17

我正在为 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 返回“未定义”,我希望它返回查询的结果。我确定我可以采用某种阻止机制,但我想知道做这样的事情的正确方法。

3个回答

ES 6(节点 8+)

您可以使用异步/等待

await operator 暂停异步函数的执行,直到 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 上存在一些问题。

快乐编码:)

我提交了一个错误。显然,您必须从 mongo-sync 库中的 node_modules 中删除纤维module。貌似是包装问题。
2021-04-18 05:04:17
我用 mongo-sync 编写了一个 5 行脚本,但它失败了,尽管它几乎完美地匹配了他们的测试代码。好像有bug。
2021-05-03 05:04:17
@jcollum :您能描述一下您遇到的确切问题吗?因为它对我有用,没有大问题..如果你确定它是module中的一个错误,你可以在Repo上提出一个新问题
2021-05-13 05:04:17
如果它依赖于节点光纤,那么它不是同步的
2021-05-17 05:04:17

没有办法在没有某种可怕的黑客攻击的情况下使这种同步。正确的方法是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现在提供了一个同步编码的方式风格使用异步API,这些API返回的Promise(如本地的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.

您可以随时使用异步库,以避免出现“doom” github.com/caolan/async这将使代码更具可读性和美观性。
2021-04-24 05:04:17
@user1063287 是的。有关更多示例,请参见此处
2021-04-24 05:04:17
返回的thing是一个“[object Promise]”。我们如何读取 promise 对象中的数据?(无.then回调)
2021-04-26 05:04:17
@Logan 我不会称其为“解决方法”,这就是 node 的设计方式。
2021-05-01 05:04:17
感谢约翰尼的这个解决方法!我希望有一种开箱即用的简单方法……即使编写一个简单的if_exists()函数也令人沮丧……顺便说一句,如果有人知道更简单的方法,或者驱动程序的更新,请在此处发布。
2021-05-14 05:04:17

虽然它不是严格同步的,但我反复采用并发现非常有用的一种模式是在异步函数上使用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. 您可以将结果作为Promise传递给任何其他代码