12

Right now, the parent for-loop (m < repliesIDsArray.length) completes before the first findOne fires, so this all only loops through the last element of the repliesIDsArray..asynchronous..

What's the proper syntax for a promisified version of this codeset? Am new to promisification, and wondering how to start this promisify + loop through arrays + account for if-statements..

Bluebird is required, and Promise.promisifyAll(require("mongoose")); is called.

for(var m=0; m<repliesIDsArray.length; m++){

objectID = repliesIDsArray[m];

Models.Message.findOne({ "_id": req.params.message_id},
    function (err, doc) {
        if (doc) {
         // loop over doc.replies to find the index(index1) of objectID at replies[index]._id
         var index1;
         for(var i=0; i<doc.replies.length; i++){
            if (doc.replies[i]._id == objectID) {
                index1 = i;
                break;
            }
         }
         // loop over doc.replies[index1].to and find the index(index2) of res.locals.username at replies[index1].to[index2]
         var index2;
         for(var j=0; j<doc.replies[index1].to.length; j++){
            if (doc.replies[index1].to[j].username === res.locals.username) {
                index2 = j;
                break;
            }
         }

         doc.replies[index1].to[index2].read.marked = true;
         doc.replies[index1].to[index2].read.datetime = req.body.datetimeRead;
         doc.replies[index1].to[index2].updated= req.body.datetimeRead;
         doc.markModified('replies');
         doc.save();
    }
}); // .save() read.marked:true for each replyID of this Message for res.locals.username

} // for loop of repliesIDsArray
Yves M.
  • 29,855
  • 23
  • 108
  • 144
StackThis
  • 883
  • 3
  • 15
  • 45
  • You can use `Promise.each` with `.fineOneAsync` – Benjamin Gruenbaum Aug 04 '14 at 08:48
  • @BenjaminGruenbaum Thanks for the heads up.. Testing this: `Promise.each(function(repliesIDsArray) { console.log('is repliesIDsArray here now equivalent to repliesIDsArray[i] ? ' + repliesIDsArray ); });` logs this though: `Possibly unhandled TypeError: fn must be a function` .. would definitely appreciate an example of how to start this – StackThis Aug 04 '14 at 16:42

1 Answers1

35

As Benjamin said, instead of using for loop, use Promise.each (or .map)

Look on the Bluebird API docs here and search "example of static map:". With map is clearer to understand than docs for each

var Promise = require('bluebird')
// promisify the entire mongoose Model
var Message = Promise.promisifyAll(Models.Message)

Promise.each(repliesIDsArray, function(replyID){
    return Message.findOneAsync({'_id': req.params.message_id})
        .then(function(doc){
            // do stuff with 'doc' here.  
        })
})

From the docs, .each (or .map) takes "an array, or a promise of an array, which contains promises (or a mix of promises and values)", so that means you can use it with array of 100% pure values to kickoff promise chain

Hope it helps!

aarosil
  • 4,848
  • 3
  • 27
  • 41
  • This will not work, you must return the promise in the function in the `.each`, otherwise it has no way of knowing the promise is done. If you have any suggestions about the docs I'd love to hear them and edit accordingly. – Benjamin Gruenbaum Aug 05 '14 at 05:47
  • thanks good catch I corrected. Docs are great only thing I would add is 'static' example for `.each` maybe. I pointed him to the 'static map' section since it has the example of starting out promise wit an array – aarosil Aug 05 '14 at 06:44
  • Glad I could help. Enjoy Bluebird, if you have any feedback, suggestions etc we'd love to hear it. – Benjamin Gruenbaum Aug 05 '14 at 17:16
  • Benjamin, I find new gems & get inspired nearly every time I read through the Bluebird API doc. Thanks!!!!! – aarosil Aug 20 '14 at 18:38
  • @aarosil - I'm new to nodejs and still learning the concept of promises. I have a question in follow-up to your response. What if I want to execute a code after ensuring that Message.findOneAsync has completed execution in the last iteration of Promise.each ( ... ). In my testing so far, I noticed that Promise.each returns immediately after calling Message.findOneAsync in the last iteration. I want it to wait until Message.findOneAsync has completed execution in the last iteration. I'm not able to figure out how to do so. Thanks. – DFB May 24 '15 at 17:15
  • Just return another promise from inside there. It will only continue without waiting if the then doesn't turn another promise. If this doesn't help answer please create a new question with sample code. – aarosil May 24 '15 at 17:24
  • 1
    Thanks for your quick response @aarosil . Actually it was working correctly. I had other issues in the code. It's working fine now. – DFB May 26 '15 at 06:33
  • @aarosil One thing that I still couldn't figure out was a way to "break" out of the .each loop. I can always set a flag when a certain condition is met and check that at the beginning of each iteration, but that's not a neat way. Is there a better way to break out? I looked into the documentation, but couldn't figure it out. Thanks. – DFB May 29 '15 at 01:27
  • @DFB that's correct it's not possible. You may want to check out a different approach like this: http://stackoverflow.com/a/24270363/2845029 – aarosil May 29 '15 at 21:24
  • Does .each expose access to the index, like a map or forEach would? @aarosil – Peege151 May 26 '16 at 18:22