Search code examples
javascriptnode.jsmongoosepromisebluebird

Cannot get nested promises to return value to next promise chain


Below, you'll see where i'm having an issue in the commented code. I have a nested promise which creates an object in a collection, then returns it.

But, I think i'm having an issue with async. The function completes before the created object is returned by the nested promise.

I'm still grasping promise chaining, and i'm probably doing a lot wrong here. Much of this may be cleared up if I could clean / flatten some of this.

 PostSchema.statics.createPost = function (o, user) {
    var whiskey;
    return Whiskey
        .createWhiskey(o.whiskey.value)
        .then(function (whiskeyData) {
            whiskey = whiskeyData.whiskey;
            o.post.whiskey = whiskeyData.whiskey._id;

            if (whiskeyData.whiskey.distiller) {
                o.post.distiller = whiskeyData.whiskey.distiller;
            }

            return o.distiller.new === true && !whiskey.distiller ? Distiller.newDistiller(o.distiller.value) : Promise.resolve()
                .then(function (distiller) {
                    //this never invokes   <---- it's called from the function below
                    console.log('never invokes', distiller).
                    if (distiller) {
                        whiskey.distiller = distiller._id;
                        //test this save
                        whiskey.save();
                        o.post.distiller = distiller._id;
                    }

                    var post = o.post;
                    post.user = user._id;
                    return Post
                        .createAsync(post)
                        .then(function (data) {
                            return Post
                                .populate(data, {
                                    path: 'user whiskey',
                                    populate: {
                                        path: 'distiller style',
                                    }
                                })
                        })

                        .then(function (populatedData) {
                            return (user.shareFB ? social.checkFB(user, populatedData) : Promise.resolve())
                                .then(function (FBres) {
                                    return (user.shareTWT ? social.checkTWT(user, populatedData) : Promise.resolve())
                                        .then(function (TWTres) {
                                            var socialData = [TWTres, FBres];
                                            return {
                                                'post': populatedData,
                                                'social': socialData
                                            };
                                        })
                                })
                        })
                })
        })
        .catch(function (err) {
            console.log('post create err : ', err);
        })
};

this is where the distiller is created and attempting to return:

DistillerSchema.statics.newDistiller = function (o) {

    return Distiller
        .findAsync({
            'name': o.name
        })
        .then(function (distiller) {
            if (distiller.length) {
                return distiller[0];
            }
            return Distiller
            .createAsync(o)
            .then(function (data) {
                //console.log here indicates that is is created <-- created and returned here
                console.log('distiller created ', data)
                return data;
            })
        })
        .catch(function(err) {
            console.log('create distiller err ', err);
        })
};

Solution

  • Sounds like a grouping mistake. Instead of

    return o.distiller.new === true && !whiskey.distiller
      ? Distiller.newDistiller(o.distiller.value)
      : Promise.resolve().then(…) // callback only called when no new distiller
    

    you want

    return (o.distiller.new && !whiskey.distiller
      ? Distiller.newDistiller(o.distiller.value)
      : Promise.resolve()
    ).then(…) // callback always called
    

    At least that is how did all the other conditionals :-)

    I'm still grasping promise chaining

    After having learned that you always need to return from your asynchronous functions (which you did fine), you should have a look at how it is possible to flatten a chain (which applies to your code when all the conditionals are local expressions only, not early returns). Also I'd recommend not to indent chained method invocations as that quickly gets out of hand when nesting then callbacks, but that's just my personal preference.