在 Node.js 中进行同步 MongoDB 查询的正确方法是啥?
Posted
技术标签:
【中文标题】在 Node.js 中进行同步 MongoDB 查询的正确方法是啥?【英文标题】: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 返回“未定义”,我希望它返回查询结果。我确信我可以使用某种阻塞机制,但我想知道执行此类操作的正确方法。
【问题讨论】:
【参考方案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】:
虽然它不是严格同步的,但我反复采用并发现非常有用的一种模式是在异步函数上使用 co 和 promisify 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 =>
);
这意味着:
-
您可以使用任何异步库编写类似“同步”的代码
从回调中抛出错误,这意味着您不需要成功检查
您可以将结果作为承诺传递给任何其他代码
【讨论】:
以上是关于在 Node.js 中进行同步 MongoDB 查询的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章
node.js+express+mongoose实现用户增删查改案例