Mongoose: Add 'promise' return value to model save operation

Created on 16 Apr 2013  ·  50Comments  ·  Source: Automattic/mongoose

Add promise return support to save operation, currently returns undefined. There are multiple return paths depending on validation and save state that would need to be updated. This would allow chaining of multiple save operations with .then() instead of nesting.

enhancement

Most helpful comment

For now I am doing this:

userSchema.methods.saveAsync = function() {
  return new Promise((resolve,reject) => {
    this.save((err) => {
      if(err) return reject(err)
      resolve()
    })
  })
}

All 50 comments

I'm adding this to the list for 4.x. Relates to how middleware is implemented (hooks.js).

Awesome, thanks. I've implemented a version of jquery's .when as an extension of the mongoose Promise object for my project. Is that something you'd be interested in having a pull request for?

var p1 = Users.find();
var p2 = Animals.find();
Promise.when(p1, p2).addBack(function(err, users, animals) {
//etc
});

No thanks. I really don't want to add more features that can be implemented outside of mongoose. Its already bloated.

For others that find this thread, here it is: https://github.com/wshaver/mongoosewhen

@wshaver publish that to npm and add a mongoose tag so it shows up on http://plugins.mongoosejs.com

Moved to here to conform to other Mongoose plugin's usage of dashes in the name: https://github.com/wshaver/mongoose-when

Published to npm, tagged. Not showing up yet but perhaps there's a delay.

@wshaver cool. it updates once a day.

After digging into #1446 I think I can achieve this without removing "hooks.js".
Green light?

go for it

On Monday, October 7, 2013, Refael Ackermann wrote:

After digging into #1446https://github.com/LearnBoost/mongoose/issues/1446I think I can achieve this without removing "hooks.js".
Green light?


Reply to this email directly or view it on GitHubhttps://github.com/LearnBoost/mongoose/issues/1431#issuecomment-25792310
.

Aaron
@aaronheckmann https://twitter.com/#!/aaronheckmann
soundcloud.com/ajhecky
github.com/aheckmann

Big PR ready

This would really help me, too. It should be possible to call myDoc.save().exec() - so basically save should return a query that can be executed. Or what is the reason, that Save does not return a Query?

+1 for promise return if we omit the callback

The fix is in the master branch, planned to be included in 3.10.

Does this include a returned promise for the model create operation?

@GDownes create() already returns a promise :) http://mongoosejs.com/docs/api.html#model_Model.create

any planned release date for 3.10? :)

@swayf 4.0 is still a few months out. My line in the sand is September 2, but hopefully before then :)

Is this still scheduled for an upcoming version?

Yeah this will be in 4.0, however, still don't have a release date for it. Last estimate was not quite right :(

:+1:

+1 because:

I need to save 2 documents in a mocha test before hook, and this functionality would allow me to execute the done() callback once both models are saved.

I tried using the Collection.insert([docs], done) method, but it does not trigger Schema.pre('save') logic that I need to hash some values.

@arathael you can always use the async module to save your two documents in async.parallel and execute done in the callback

Thanks man, I'm actually testing that now. I just jumped in because I think native support from ODM would rock.

+1

I use the experimental ES7 await/async using 6to5. If record.save() returned a promise, I can do this:

try {
  await record.save()
} catch(err) {
  ...
}

For now I am doing this:

userSchema.methods.saveAsync = function() {
  return new Promise((resolve,reject) => {
    this.save((err) => {
      if(err) return reject(err)
      resolve()
    })
  })
}

+1 for @aaronshaf , works great for me, I'd write it also in ES5 for those who aren't using ES6. Thanks

Here is what I did for now. Obviously Q is not needed but that is what I use for promises elsewhere.

/*
 * Hack until mongoose 3.10 comes out. See this: https://github.com/LearnBoost/mongoose/issues/1431
 */
mongoose.Document.prototype.savePromise = function () {
    var that = this;
    return Q.Promise(function(resolve, reject) {
        that.save(function (err, item, numberAffected) {
            if (err) {
                reject(err);
            }
            resolve(item, numberAffected);
        });
    });
};

mongoose.Document.prototype.removePromise = function () {
    var that = this;
    return Q.Promise(function(resolve, reject) {
        that.remove(function (err, item) {
            if (err) {
                reject(err);
            }
            resolve(item);
        });
    });
};

+1 on the hack @jhullfly however I think Q's resolve only takes one argument so numberAffected is dropped, need to put them in an array or something to get access to both:

mongoose.Document.prototype.savePromise = function () {
    var that = this;
    return Q.Promise(function(resolve, reject) {
        that.save(function (err, item, numberAffected) {
            if (err) {
                reject(err);
            }
            resolve([item, numberAffected]);
        });
    });
};

1+ for promise being returned from save!

Now save returns promise in 4.0.1. Cheers.

+1

@arathael +1

Are there documentation related to how save is used with Promises?

I tried the following and it didn't seem to work:

var User = mongoose.model('User');
var u = new User();
u.save().then(function() {
// never gets here.
});
// I even tried the following, and neither works.
u.save().then(function() {}).end();

Are you on mongoose 4.0.1 and did you npm install it?

@Ouwen Yes I'm running 4.0.1 and yes i did or else i would've gotten an error if mongoose wasn't installed.

The problem is that then seems to never get called. I am pretty sure I am doing something weird (or else there would be bugs filed quickly), but I can't reason what, that's what i'm curious to see if there are any examples/documentation around it.

Hmmm, very strange. I know that I was running 3.8.8 and forgot to npm install to up it to 4.0.1 which is why a promise was not returned from u.save()

What you have written should work.

@limianwang are you connecting successfully to a mongodb server? Can you provide me a standalone example that reproduces this?

It seems that promise object returned by .save() is not the same as other promises, it has catch/caught instead of onReject.

mongoose 4.1.10 and ...

var u = new User();
u.save().then(...)

still does not return a promise

@xrado please provide a more substantial example. We have tests in place to show that should work in mongoose 4.1.10.

@vkarpov15 my bad, I have try it again and works ..I don't know what I was doing before ..sorry

I have the same issue, Save saves the data to the db right away and does not call the code in the then. The strange thing is that I had another peace of code that did call the then block and the promise worked.

Code ( the user.save() part saves it, then is not defined but never called )

user.save().then( function(userX) { // save works
console.log(2); // this is not called
}).end() // not called
.then(undefined, function(err){
console.log('err:' + err ); // not called
});

Update i used the old way and it works like:

user.save(function (err, newuser) {
if (err) console.log('err=' + err);
console.log('user saved');
});

WTF: The solution is was to simply change the version of mongoose to ^4.1.9 from "3.8.X"

Then user.save().then( function(userX) { // save works
console.log(2);
}).end()
.then(undefined, function(err){
console.log('err:' + err );
});

WORKS!!!!!!!!!!! sweet....

@nickjohngray glad you figured it out :) I strongly recommend you read the promises guide on the docs before continuing to use promises with mongoose though - don't worry, its a short read.

Im having the same issue... Id really hope I dont have to downgrade an entire major release just to get a save to work inside a promise.

@jhyland87 please provide code samples so I can see what the issue is.

@vkarpov15 I did, in the ticket

This was fixed in the other ticket...

Basically, this code:

var User = mongoose.model('User');
var u = new User();
u.save().then(function() {
// never gets here.
});

Just needed a return added when saving a doc.. so:

var User = mongoose.model('User');
var u = new User();
return u.save().then(function() {
// never gets here.
});

@jhyland87, FYI - another example of chaining promises with Mongoose.

Is this still an issue?

I have the following code (using TypeScript)...

User.findById(id)
.exec()
.then((user) => {
  user.name = 'new name from wherever!';
  return user.save();
})
.then((user) => {
  // want to do more stuff with the user object once it has been saved successfully...
  // ... but it never makes it here!!!
};

However, when I add an empty callback to the save() function, then all seems well, as below...

User.findById(id)
.exec()
.then((user) => {
  user.name = 'new name from wherever!';
  return user.save(() => {
    // unwanted empty callback... hmmm?
  });
})
.then((user) => {
  // we made it here!!! but now...
  // user === undefined :(
};

Are you using mpromise? Try switching to mongoose.Promise = global.Promise;

Was this page helpful?
0 / 5 - 0 ratings

Related issues

simonxca picture simonxca  ·  3Comments

Soviut picture Soviut  ·  3Comments

lukasz-zak picture lukasz-zak  ·  3Comments

p3x-robot picture p3x-robot  ·  3Comments

adamreisnz picture adamreisnz  ·  3Comments