Mongoose - exec函数是做什么的?

186

我发现了一段包含查询findOne和exec()函数的Mongoose代码。

我以前从没在JavaScript中见过这种方法?它到底是做什么用的?

8个回答

234
基本上使用mongoose时,可以使用helpers检索文档。每个接受查询条件的模型方法都可以通过callbackexec方法执行。 callback:
User.findOne({ name: 'daniel' }, function (err, user) {
  //
});

exec:

User
  .findOne({ name: 'daniel' })
  .exec(function (err, user) {
      //
  });

因此,当您不传递回调函数时,可以构建查询并最终执行它。
您可以在mongoose文档中找到其他信息。 更新 使用Promises与Mongoose异步操作的组合时需要注意的一点是,Mongoose查询不是Promises。查询确实会返回一个thenable,但如果您需要一个真正的 Promise,则应使用exec方法。更多信息可以在这里找到。
在更新过程中,我注意到我没有明确回答问题:

我以前从未见过JavaScript中的那种方法?它到底是做什么的?

嗯,这不是JavaScript的本地方法,而是Mongoose API的一部分。

4
如果我已经设置了mongoose.Promise = require('bluebird'),那么我是否仍然需要使用.exec()?谢谢。 - wayofthefuture
4
@wayofthefuture,我认为这个问题的文档非常令人困惑,但我认为你仍然需要调用exec方法。至少在文档中是这样做的。为了确定,你可以使用Model.find() instanceof require('bluebird')进行自查。希望能有所帮助。 - danillouz
1
然后还有其他操作的问题,比如删除和插入多个...在这些情况下是否需要使用exec()?删除将不得不在引擎盖下使用某种查询... - wayofthefuture
1
exec 命令如果没有传入回调函数,也会返回一个 Promise,这非常方便,我必须说。 - Alexander Mills
如果你认为Model.update()会完成它的工作,不必等待一些不太重要的数据被更新并返回API的响应;那么你的更新将不会执行。你需要使用Model.update().exec()来确保它会执行。然后你可以在不等待更新的情况下响应API。这可能会帮助到某些人。 - Yogesh
能否执行 .find({}) 查询并稍后再执行它?例如 const query = await Model.find({}); 然后稍后执行 query.exec(); - Matthew Wolman

118

我从来不使用exec()函数对模型进行CRUD(Create, Read, Update, Delete)操作。当我需要对模型进行CRUD操作时,我会这样使用:

const user = await UserModel.findOne(userCondition);

它总是能完成任务。所以我想知道"exec()用于什么?"。 在mongoose文档中搜索,我发现答案在这里

你应该使用await exec()吗?

以下是故事的背景。
您有两种方法可以对模型执行查询:使用回调或使用exec()函数。但也可以使用awaitexec()函数返回一个promise,您可以将其与then()async/await一起使用来在模型上“异步”执行查询。那么问题是:"如果我只想使用user = await UserModel.find()并且它能够正常工作,那么为什么要使用exec()函数呢?"。在文档中可以找到答案:

使用awaitexec()与不使用它们相比,有两个不同之处。

  • 从功能角度来看,使用awaitexec()与不使用它们没有区别。只是当您调用没有exec()callback的查询时,它返回一个thenable,类似于promise但不是promise。(您可以在这里找到区别)。但当您使用exec()运行查询时,会准确地得到一个promise作为响应。

// returns a thenable as response that is not a promise, but you can use await and then() with it.
const user = await UserModel.findOne(userCondition);

// returns exactly a promise.
const user = await UserModel.findOne(userCondition).exec(); 
  • 另一个区别是,如果你在exec()中使用await,当你捕获任何执行查询中的错误时,你将获得更好的“堆栈跟踪”。 所以:

    这两行代码执行相同的操作:
const user = await UserModel.findOne(userCondition);

// does exactly as the before line does, but you get a better stack trace if any error happened
const user = await UserModel.findOne(userCondition).exec(); 

1
非常重要的区别!在测试我的查询时,我注意到当我故意扭曲URL中_id字段的格式以测试错误时,try/catch没有捕获错误,而我的模型发出了弃用警告。这个警告表示未被捕获的错误将来将不再得到支持。 - Julio Spinelli
3
就功能而言,这两者是等价的。但是,我们建议使用 .exec() 因为它可以给您提供更好的堆栈跟踪。 - CodeFinity
所有的 mongoose 操作都可以使用 await 吗?我的意思是,user.deleteOne() 也能使用 await 吗? - Luis

82

Daniel已经很好地回答了这个问题。为了详细阐述构建和执行查询的详尽方法列表,请看以下用例:

查询构建

在Mongoose中,只有在调用thenexec之后,才会执行查询。当构建复杂的查询时,这非常有用。一些示例包括使用populateaggregate函数。

User.find({name: 'John'}) // Will not execute

通过回调函数执行

虽然由于其嵌套的特性而被许多人所不喜欢,但查询可以通过提供可选的回调函数来执行。

User.find({name: 'John'}, (err, res) => {}) // Will execute

将API转换为Promises/A+风格

Mongoose查询确实提供了then函数,但它与常规的promises不同。简单来说,Promises/A+规范要求then函数的工作方式与我们通常使用的promises相似。

User.find({name: 'John'}).then(); // Will execute
Promise.all([User.find({name: 'John'}), User.find({name: 'Bob'})]) // Will execute all queries in parallel

exec 函数

来自 Mongoose 文档 如果您需要完整的承诺,请使用 .exec() 函数。

User.find({name: 'John'}).exec(); // Will execute returning a promise

1
文档没有明确提到,但示例显示User.save()也返回一个Promise。除了exec()和save()之外,模型上的其他mongoose方法是否也返回Promise,还是只有这两个方法? - thetrystero
如上所述,您可以在查询中使用then来返回一个Promise。这与exec并没有太大的区别。我发现这个用例很方便,特别是在使用类似于Promise.all的东西时。不确定exec返回的Promise是否适用于这种情况。 - Anshuul Kai
请问您能否添加一个包含 .exec() 的完整示例?@AnshulKoka .exec() 是否会自动使查询异步化,还是我需要添加 async/await? - O'Dane Brissett
将查询设置为异步操作需要使用async/await,因此我不确定我是否理解了问题。只需在我的示例前面添加await即可获得完整示例。 - Anshuul Kai
请点击此处查看解释:https://dev59.com/dVYO5IYBdhLWcg3wDNQb#46457247 - DragonFire
Promise.all 的例子可能过于简单了,你应该使用 find(name: { $in: ['John', 'Bob'] }) - OneCricketeer

11

exec()如果没有提供回调函数,将返回一个promise。因此,以下模式非常方便和通用 - 它可以很好地处理回调或promise:

function findAll(query, populate, cb) {

  let q = Response.find(query);

  if (populate && populate.length > 0) {
    q = q.populate(populate);
  }

  // cb is optional, will return promise if cb == null
  return q.lean().exec(cb);

}

我建议在Mongoose中使用Bluebird Promise,要这样做,请使用以下调用:

const mongoose = require('mongoose');
mongoose.Promise = require('bluebird');

4
为什么我们需要 Bluebird?当我们已经有 exec() 时。 - Indraraj26
我们现在不需要,至少是因为承诺已经成为一个成熟的 ES 规范,并且支持半现代浏览器。 - XML

3

所有的答案都是正确的,但最简单的方法是使用现代的 async/await 方法。

async ()=> {
const requiresUser = await User.findByIdAndUpdate(userId,{name:'noname'},{ new:true}).exec()

1

获取数据的一种方法:

find().exec((err,data)=>{

})

其他方法:

const res=await find()

1
目前你的回答不够清晰。请编辑并添加更多细节,以帮助其他人理解它如何回答所提出的问题。你可以在帮助中心找到有关如何撰写好答案的更多信息。 - Community

1

基本上,mongoose查询不会返回任何承诺。因此,如果我们希望它们像承诺一样工作,我们使用exec函数。


你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Albert Logic Einstein
这并没有回答问题。一旦你拥有足够的声望,你就可以评论任何帖子;相反,提供那些不需要向提问者澄清的答案。- 来自审核 - Webdeveloper_Jelle

0
2023年更新
Query.prototype.exec()不再接受回调函数
请使用以下模式:
const result = await Model.findOne({filter:filter}).exec();
return result ;

**or**

const x = await Model.findOne({filter:filter}).exec()
.then((data)=>{
   return (data);
}).catch((err)=> {
     return (err);
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接