Promises
Built-in Promises
Mongoose async operations, like .save() and queries, return thenables. This means that you can do things like MyModel.findOne({}).then() and await MyModel.findOne({}).exec() if you're using async/await.
You can find the return type of specific operations in the api docs You can also read more about promises in Mongoose.
const gnr = new Band({ name: 'Guns N\' Roses', members: ['Axl', 'Slash'] }); const promise = gnr.save(); assert.ok(promise instanceof Promise); promise.then(function(doc) { assert.equal(doc.name, 'Guns N\' Roses'); });Queries are not promises
Mongoose queries are not promises. They have a .then() function for co and async/await as a convenience. If you need a fully-fledged promise, use the .exec() function.
const query = Band.findOne({ name: 'Guns N\' Roses' }); assert.ok(!(query instanceof Promise)); // A query is not a fully-fledged promise, but it does have a `.then()`. query.then(function(doc) { // use doc }); // `.exec()` gives you a fully-fledged promise const promise = Band.findOne({ name: 'Guns N\' Roses' }).exec(); assert.ok(promise instanceof Promise); promise.then(function(doc) { // use doc });Queries are thenable
Although queries are not promises, queries are thenables. That means they have a .then() function, so you can use queries as promises with either promise chaining or async await
Band.findOne({ name: 'Guns N\' Roses' }).then(function(doc) { // use doc }); Should You Use exec() With await?
There are two alternatives for using await with queries:
await Band.findOne();await Band.findOne().exec();
As far as functionality is concerned, these two are equivalent. However, we recommend using .exec() because that gives you better stack traces.
const doc = await Band.findOne({ name: 'Guns N\' Roses' }); // works const badId = 'this is not a valid id'; try { await Band.findOne({ _id: badId }); } catch (err) { // Without `exec()`, the stack trace does **not** include the // calling code. Below is the stack trace: // // CastError: Cast to ObjectId failed for value "this is not a valid id" at path "_id" for model "band-promises" // at new CastError (/app/node_modules/mongoose/lib/error/cast.js:29:11) // at model.Query.exec (/app/node_modules/mongoose/lib/query.js:4331:21) // at model.Query.Query.then (/app/node_modules/mongoose/lib/query.js:4423:15) // at process._tickCallback (internal/process/next_tick.js:68:7) err.stack; } try { await Band.findOne({ _id: badId }).exec(); } catch (err) { // With `exec()`, the stack trace includes where in your code you // called `exec()`. Below is the stack trace: // // CastError: Cast to ObjectId failed for value "this is not a valid id" at path "_id" for model "band-promises" // at new CastError (/app/node_modules/mongoose/lib/error/cast.js:29:11) // at model.Query.exec (/app/node_modules/mongoose/lib/query.js:4331:21) // at Context.<anonymous> (/app/test/index.test.js:138:42) // at process._tickCallback (internal/process/next_tick.js:68:7) err.stack; } Want to learn how to check whether your favorite npm modules work with async/await without cobbling together contradictory answers from Google and Stack Overflow? Chapter 4 of Mastering Async/Await explains the basic principles for determining whether frameworks like React and Mongoose support async/await. Get your copy!
