0

I'm working with Nodejs and i want to use promises in order to make a full response after a for loop.

exports.getAlerts = function(req,res,next){
var detected_beacons = [];
if (!req.body  || Object.keys(req.body).length == 0) {
    res.status(401);
    res.json({message:'No data sent'});
    return
}
var nets = req.body.networks;
db.collection("beaconConfig", function(err, beaconConfigCollection){
    if (!err){
        var promises = [];
        for(var i=0;i<nets.length;i++){
            var defer = q.defer();
            beaconConfigCollection.find({$or: [{"data.major" : nets[i].toString()},{"data.major" : nets[i]}], batteryLevel : {$lt : 70}}).toArray(function(errFind, saver){
                if (!errFind && saver && saver.length > 0){
                    promises.push(defer.promise);
                    console.log("--------------------savers -------------------");
                    console.log(saver);
                    for(var j=0; j<saver.length;j++){
                        console.log("--------------------saver[j]-------------------");
                        console.log(saver[j]);
                        var detected = {}
                        var major = saver[j].data.major;
                        detected.major = major;
                        console.log("--------------------detected -------------------");
                        console.log(detected);
                        detected_beacons.push(detected);
                        defer.resolve(detected);
                    }
                }
            });
        }
        q.all(promises).then(function(results){
            console.log("--------------------detected_beacons -------------------");
            console.log(detected_beacons);
            res.json(detected_beacons);
        });

    } else {
        console.error(err);
        res.status(500);
        res.json({message:"Couldn't connect to database"});
    }
});};

All the consoles.log works fine unless the last one, the ---detected_beacons--- one, which is THE FIRST ONE to be shown and it is empty.

That is the reason why i'm thinking that the promises are not working well. I have var q = require('q'); at the top and the mongo connection does not return any problem.

Thanks for the help.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
aserrin55
  • 350
  • 5
  • 15
  • 2
    Because the `promises` array is populated asynchronously. – thefourtheye Sep 01 '16 at 11:02
  • And how can i solve that? – aserrin55 Sep 01 '16 at 11:23
  • 1
    Well just put the `promises.push(defer.promise);` outside of the asynchronous callback? Btw, you've also got the [common closure in a loop problem](http://stackoverflow.com/q/750486/1048572). Better factor out the [promsification](http://stackoverflow.com/q/22519784/1048572) of `find` in a separate function. – Bergi Sep 01 '16 at 11:28
  • @Bergi You are right! It is an antipattern to reuse Promise inside a loop. – richardaum Sep 01 '16 at 21:34
  • I understand what you all are saying but i'm not able to fix the error. I have change the code many times but the first thing that is sent is the empty array – aserrin55 Sep 02 '16 at 06:09
  • I will try to write a better answer. – richardaum Sep 03 '16 at 13:24

1 Answers1

1

First of all, an awesome guide about how to get along with Promises.

Well, haters gonna hate but there is nothing wrong with Promises at all (at least, I hope so).

According to 'MongoDb for Node' documentation, .toArray() returns a Promise, like most of the methods of this library. I felt free to make some appointments along your code:

exports.getAlerts = function(req, res, next) {
    if (!req.body || Object.keys(req.body).length == 0) {
        res.status(401);
        res.json({message: 'No data sent'});
        return;
    }
    // db.collection returns a promise :)
    return db.collection("beaconConfig").then(function(beaconConfigCollection) {
        // you can use .map() function to put together all promise from .find() 
        var promises = req.body.networks.map(function(net) {
            // .find() also returns a promise. this promise will be concat with all
            // promises from each net element.
            return beaconConfigCollection.find({
                $or: [{
                    "data.major": net.toString()
                }, {
                    "data.major": net
                }],
                batteryLevel: {
                    $lt: 70
                }
            }).toArray().then(function(saver) {
                // you can use the .find() response to create an array
                // with only the data that you want.
                return saver.map(function(saverElement) {
                    // your result array will be composed using saverElement.data.major
                    return saverElement.data.major;
                });
            }).catch(function(err) {});
        });
        // use q.all to create a promise that will be resolved when all promises
        // from the array `promises` were resolved.
        return q.all(promises);
    }).then(function(results) {
        console.log("results", results);
    }).catch(function(err) {});
};

I hope it helps you!

richardaum
  • 6,651
  • 12
  • 48
  • 64