StackThis
StackThis

Reputation: 883

How to promisfy MongoDB/Mongoose .findOne before .push, in an array.forEach?

Have looked through the bluebird readMe examples, and am still wondering how to implement/convert some async code to involve promises with .then..

There are a number of ifStatements in here, though the main point is that while looping through the toArray, if the element exists in the database (findOne) then assign it to a variable to .push it to a field in an embedded doc of the new (.post & .save) db doc.

Here's the current async code that consequently runs the findOne after .save .. but it needs to run before:

// create a story (accessed at POST http://localhost:4200/api/v1/story)
.post(function(req, res) {

    console.log('posting a new Story..from: ' + res.locals._id + '..' + res.locals.username );

    var story = new Models.Story();

    var toArray = req.body.to;
    console.log(toArray); // [ 'user1', 'user2', 'user3' ]
    toArray.forEach(toArrayLoop);
    function toArrayLoop(element, index, array){

        console.log('element: ' + element); // 'user1' .. 'user2' .. 'user3'

        var out = false; // if sent to Self, out = true
        if (element == res.locals.username) {out = true; console.log('to element: ' + element + ' == res.locals.username: ' + res.locals.username)}

        var toUserId = '';
        if (element) {
            Models.User.findOne({username: element}, function (err, user) {

                if (user) {

                if (err) {
                    console.log(err);
                    res.send(err);
                }
                    console.log('user._id = ' + user._id);
                    toUserId = user._id;
                } else {
                    toUserId = '';
                    console.log('toUserId = ' + toUserId);
                }

            });
        }

        story.to.push({

            user : toUserId, // push the findOne user._id
            username : element, // push the toArray element
            view :
            {
              inbox: true,
              outbox: out,
              archive: false,
            },
            updated : req.body.nowDatetime

        });

    }

    var archive = false;
    console.log('req.body.archive = ' + req.body.archive);
    if (req.body.archive == 'true') { archive = true; console.log('archive = ' + archive); };

    var in = false;
    toArray.forEach(fromSelfLoop);
    function fromSelfLoop(element, index, array){
        console.log('checking if sent to Self: ' + element); // 'user1' .. if matches res.locals: (sent from/to Self)
        if (element == res.locals.username) {in = true; console.log('from element: ' + element + ' == res.locals.username: ' + res.locals.username)}
    } // if sent to Self, archive = true

    story.from.push({

        user : res.locals._id,
        username : res.locals.username,
        view :
        {
          inbox: in,
          outbox: true,
          archive: archive,
        },
        updated : req.body.nowDatetime

    });

    story.title = req.body.title;
    // ..even more doc val assignments..

    console.log('To: ' + req.body.to);
    console.log('Story: ' + req.body.title);

    story.save(function(err, result) {

        if (err) {
            console.log(err);
            res.send(err);
        }
        console.log("The result: ", result);
            res.json({ message: 'Story "' + story.title + '" Created' });

    });

    console.log('post success!');

})

Upvotes: 0

Views: 1258

Answers (1)

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276306

You're way overkilling it in my opinion, Promises provide ways to synchronize this out of the box seamlessly.

You can use promise aggregation methods (in this case .join and .props) to map directly to the properties and get the values.

Assuming you promisified Mongoose (so Bluebird promises rather than Mongoose ones).

var story = new Models.Story();
var toArray = req.body.to; // [ 'user1', 'user2', 'user3' ]
var to = Promise.map(toArray,function(element){
    return Promise.props({ // resolves all properties
        user : Models.User.findOneAsync({username: element}), 
        username : element, // push the toArray element
        view : {
            inbox: true,
            outbox: element == res.locals.user.username,
            archive: false
        },
        updated : req.body.nowDatetime
    });
});
var from = Promise.map(toArray,function(element){ // can be a normal map
    return Promise.props({
        user : res.locals._id,
        username : res.locals.username,
        view : {
            inbox: element == res.locals.user.username,
            outbox: true,
            archive: archive,
        },
        updated : req.body.nowDatetime
    });
});
Promise.join(to, from, function(to, from){
    story.to = to;
    story.from = from;
    story.title = req.body.title;
    return story.save(); 
}).then(function(){
    console.log("Success! Story saved!");
}).catch(Promise.OperationalError, function(e){
    // handle error in Mongoose save findOne etc, res.send(...)
}).catch(function(e){
    // handle other exceptions here, this is most likely
    // a 500 error where the top one is a 4XX, but pay close
    // attention to how you handle errors here
});

Upvotes: 2

Related Questions