How to use "q" module for refactoring mongoose code?

asynchronous, javascript, mongoose, promise, q

Solution

You'll want to use `Q.nfcall`, documented in the README and the Wiki. All Mongoose methods are Node-style. I'll also use `.spread` instead of manually destructuring `.then`.

var mongoose = require('mongoose');
mongoose.connect('mongo://localhost/test');
var conn = mongoose.connection;

var users = conn.collection('users');
var channels = conn.collection('channels');
var articles = conn.collection('articles');

function getInsertedArticles() {
    return Q.nfcall(users.insert.bind(users), [{/*user1*/},{/*user2*/}]).spread(function (user1, user2) {
        return Q.nfcall(channels.insert.bind(channels), [{userId:user1._id},{userId:user2._id}]).spread(function (channel1, channel2) {
            return Q.nfcall(articles.insert.bind(articles), [{userId:user1._id,channelId:channel1._id},{}]);
        });
    })
}

getInsertedArticles()
    .spread(function (article1, article2) {
        // you only get here if all three of the above steps succeeded
    })
    .fail(function (error) {
        // you get here if any of the above three steps failed
    }
);

In practice, you will rarely want to use `.spread`, since you usually are inserting an array that you don't know the size of. In that case the code can look more like this (here I also illustrate `Q.nbind`).

To compare with the original one is not quite fair, because your original has no error handling. A corrected Node-style version of the original would be like so:

var mongoose = require('mongoose');
mongoose.connect('mongo://localhost/test');
var conn = mongoose.connection;

function getInsertedArticles(cb) {
    // insert users
    conn.collection('users').insert([{/*user1*/},{/*user2*/}], function(err, docs) {
        if (err) {
            cb(err);
            return;
        }

        var user1 = docs[0], user2 = docs[1];

        // insert channels
        conn.collection('channels').insert([{userId:user1._id},{userId:user2._id}], function(err, docs) {
            if (err) {
                cb(err);
                return;
            }

            var channel1 = docs[0], channel2 = docs[1];

            // insert articles
            conn.collection('articles').insert([{userId:user1._id,channelId:channel1._id},{}], function(err, docs) {
                if (err) {
                    cb(err);
                    return;
                }

                var article1 = docs[0], article2 = docs[1];

                cb(null, [article1, article2]);
            }
        });
    };
}

getInsertedArticles(function (err, articles) {
    if (err) {
        // you get here if any of the three steps failed.
        // `articles` is `undefined`.
    } else {
        // you get here if all three succeeded.
        // `err` is null.
    }
});

Problem

I'm using mongoose to insert some data into mongodb. The code looks like: ``` var mongoose = require('mongoose'); mongoose.connect('mongo://localhost/test'); var conn = mongoose.connection; // insert users conn.collection('users').insert([{/*user1*/},{/*user2*/}], function(err, docs) { var user1 = docs[0], user2 = docs[1]; // insert channels conn.collection('channels').insert([{userId:user1._id},{userId:user2._id}], function(err, docs) { var channel1 = docs[0], channel2 = docs[1]; // insert articles conn.collection('articles').insert([{userId:user1._id,channelId:channel1._id},{}], function(err, docs) { var article1 = docs[0], article2 = docs[1]; } }); }; ``` You can see there are a lot of nested callbacks there, so I'm trying to use q to refactor it. I hope the code will look like: ``` Q.fcall(step1) .then(step2) .then(step3) .then(step4) .then(function (value4) { // Do something with value4 }, function (error) { // Handle any error from step1 through step4 }) .end(); ``` But I don't know how to do it.

Original source