Bluebird Promisfy.each, with for-loops and if-statements?
Asked Answered
T

1

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
Torrlow answered 3/8, 2014 at 23:42 Comment(2)
You can use Promise.each with .fineOneAsyncCabanatuan
@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 thisTorrlow
E
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!

Ecstatic answered 5/8, 2014 at 1:16 Comment(10)
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.Cabanatuan
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 arrayEcstatic
Glad I could help. Enjoy Bluebird, if you have any feedback, suggestions etc we'd love to hear it.Cabanatuan
Benjamin, I find new gems & get inspired nearly every time I read through the Bluebird API doc. Thanks!!!!!Ecstatic
@Ecstatic - 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.Vail
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.Ecstatic
Thanks for your quick response @Ecstatic . Actually it was working correctly. I had other issues in the code. It's working fine now.Vail
@Ecstatic 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.Vail
@Vail that's correct it's not possible. You may want to check out a different approach like this: https://mcmap.net/q/257579/-while-loop-with-promisesEcstatic
Does .each expose access to the index, like a map or forEach would? @EcstaticBrendanbrenden

© 2022 - 2024 — McMap. All rights reserved.