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.

  1. const gnr = new Band({
  2. name: "Guns N' Roses",
  3. members: ['Axl', 'Slash']
  4. });
  5. const promise = gnr.save();
  6. assert.ok(promise instanceof Promise);
  7. promise.then(function (doc) {
  8. assert.equal(doc.name, "Guns N' Roses");
  9. });

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.

  1. const query = Band.findOne({name: "Guns N' Roses"});
  2. assert.ok(!(query instanceof Promise));
  3. // A query is not a fully-fledged promise, but it does have a `.then()`.
  4. query.then(function(doc) {
  5. // use doc
  6. });
  7. // `.exec()` gives you a fully-fledged promise
  8. const promise = Band.findOne({name: "Guns N' Roses"}).exec();
  9. assert.ok(promise instanceof Promise);
  10. promise.then(function (doc) {
  11. // use doc
  12. });

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

  1. Band.findOne({name: "Guns N' Roses"}).then(function(doc) {
  2. // use doc
  3. });

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.

  1. const doc = await Band.findOne({ name: "Guns N' Roses" }); // works
  2. const badId = 'this is not a valid id';
  3. try {
  4. await Band.findOne({ _id: badId });
  5. } catch (err) {
  6. // Without `exec()`, the stack trace does **not** include the
  7. // calling code. Below is the stack trace:
  8. //
  9. // CastError: Cast to ObjectId failed for value "this is not a valid id" at path "_id" for model "band-promises"
  10. // at new CastError (/app/node_modules/mongoose/lib/error/cast.js:29:11)
  11. // at model.Query.exec (/app/node_modules/mongoose/lib/query.js:4331:21)
  12. // at model.Query.Query.then (/app/node_modules/mongoose/lib/query.js:4423:15)
  13. // at process._tickCallback (internal/process/next_tick.js:68:7)
  14. err.stack;
  15. }
  16. try {
  17. await Band.findOne({ _id: badId }).exec();
  18. } catch (err) {
  19. // With `exec()`, the stack trace includes where in your code you
  20. // called `exec()`. Below is the stack trace:
  21. //
  22. // CastError: Cast to ObjectId failed for value "this is not a valid id" at path "_id" for model "band-promises"
  23. // at new CastError (/app/node_modules/mongoose/lib/error/cast.js:29:11)
  24. // at model.Query.exec (/app/node_modules/mongoose/lib/query.js:4331:21)
  25. // at Context.<anonymous> (/app/test/index.test.js:138:42)
  26. // at process._tickCallback (internal/process/next_tick.js:68:7)
  27. err.stack;
  28. }

Plugging in your own Promises Library

If you’re an advanced user, you may want to plug in your own promise library like bluebird. Just set mongoose.Promise to your favorite ES6-style promise constructor and mongoose will use it.

  1. // Use bluebird
  2. mongoose.Promise = require('bluebird');
  3. const bluebirdPromise = Band.findOne({name: "Guns N' Roses"}).exec();
  4. assert.equal(bluebirdPromise.constructor, require('bluebird'));
  5. // Use q. Note that you **must** use `require('q').Promise`.
  6. mongoose.Promise = require('q').Promise;
  7. const qPromise = Band.findOne({name: "Guns N' Roses"}).exec();
  8. assert.ok(qPromise instanceof require('q').makePromise);

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!

Promises - 图1