简体   繁体   中英

Why is JS promise not working with mongoose?

var mongoose = require('mongoose');
import es6Promise from 'es6-promise';
mongoose.Promise = es6Promise.Promise;
const follow = (followerID, toFollowId, cb) => { //REVISE only update
    User.update(
        { _id: toFollowId},
        {$push: {usersFollowing: followerID}},
        function(err){
            if (err){
                cb(true);
            } else {
                User.findByIdAndUpdate(
                    followerID,
                    {$push: {usersBeingFollowed: toFollowId}},
                    {safe: true, new: true},
                    function(err, model){
                        if (err){
                            cb(true);
                        } else {
                            cb(null, model);
                        }
                    }
                )
            }
        }
    )
}

const unfollow = (unfollowerId, toUnfollowId, cb) => { //REVISE only update
    User.update(
        { _id: toUnfollowId},
        {$pull: {usersFollowing: unfollowerId}}).then(
        function(err){
            if (err){
                return  cb(true);
            } else {
                User.findByIdAndUpdate(
                    unfollowerId,
                    {$pull: {usersBeingFollowed: toUnfollowId}},
                    {safe: true, new: true},
                    function(err, model){
                        if (err){
                            cb(true);
                        } else {
                            cb(null, model)
                        }
                    }
                )
            }
        })
}

My follow function, which doesn't use a promise works fine. I tried editing my unfollow function to work as a promise, but it doesn't work. I haven't touched JS since ES5 but my promise understanding is that I just move the callback inside .then() and call it a day. What am I missing here?

The Mongoose query update method will not execute if you don't pass it the callback function. This is stated in the docs on update :

The operation is only executed when a callback is passed. To force execution without a callback, we must first call update() and then execute it by using the exec() method.

So add .exec() to the chain, which will also return a full-fledged promise.

The promise's then method takes two callback functions, the second one will be called in case of error, so you have to split success and failure code. And to be consistent you should completely switch over to using promises, and abandon the traditional callback pattern. So the unfollow function should also itself return a promise:

const unfollow = (unfollowerId, toUnfollowId) =>
    User.update(
        { _id: toUnfollowId },
        { $pull: { usersFollowing: unfollowerId } }
    ).exec()
    .then( _ =>
        User.findByIdAndUpdate(
            unfollowerId,
            { $pull: { usersBeingFollowed: toUnfollowId } },
            { safe: true, new: true }
        ).exec()
    );

You would call that as:

unfollow(unfollowerId, toUnfollowId).then( model => {
    // success
}, err => {
    // failure
});

I just move the callback inside .then() and call it a day. What am I missing here?

That the callback convention changes as well. While node callbacks have (err, result) arguments, promises use two distinct callbacks - one for fulfillment and one for rejection - that get passed only one argument.

While you could in theory do

User.update(…).then(function(_) {
    User.findByIdAndUpdate(…);
}, function(err){
    cb(true);
});

that would be an awful practise. To leverage the true power of promises , you need to return and chain them. In your case, that would be

function unfollow(unfollowerId, toUnfollowId) {
//                                          ^ no more callback
    return User.update(
//  ^^^^^^ return the result of the `then` call
        { _id: toUnfollowId},
        {$pull: {usersFollowing: unfollowerId}}
    ).then(_ => {
        return User.findByIdAndUpdate(
//      ^^^^^^ just return the promise
            unfollowerId,
            {$pull: {usersBeingFollowed: toUnfollowId}},
            {safe: true, new: true}
//          that you get when no longer passing a callback
        );
    });
}

By not passing any error callbacks, rejections will automatically bubble down the chain, and you don't need to care about forwarding all errors explicitly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM