0

I have a nodejs Mongodb code like this . I'm trying to push into a global array and return all my results but the array contains only one value . Any idea why ?

var s = [];
var p = function(){
return new Promise(function(resolve, reject) {
    MongoClient.connect(url, function (err, db) {

   db.listCollections().forEach(function(collname) {

   db.collection(collname.name).find().sort( { duration_in_seconds: 1 }).limit(1).toArray(

  function(err, docs) {

        assert.equal(null, err);
        s.push(docs);
        resolve();
      });


});

 });

});

}


p().then(function() {

  console.log(s);

});
Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
karthik006
  • 886
  • 9
  • 19
  • You cannot reference a variable outside of the scope of the promise like that. Work with it "inside" instead. [Read the duplicate and understand it.](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Neil Lunn Jun 15 '17 at 01:46
  • Can you please explain how ? I'm new to promises @NeilLunn – karthik006 Jun 15 '17 at 01:48
  • @NeilLunn the problem here is not the promise, it's the `forEach` loop – Bergi Jun 15 '17 at 02:09

1 Answers1

2

You are resolving the promise when the first collections returns its document. You'd need to wait for all of them. Instead of wrapping everything in a large new Promise, promisify every asynchronous function on its own, and make the returned promise fulfill with the result value instead of putting it in a global variable:

function connect(url) {
    return new Promise(function(resolve, reject) {
        MongoClient.connect(url, function (err, db) {
            if (err) reject(err);
            else resolve(db);
        });
    });
}
function getArray(cursor) {
    return new Promise(function(resolve, reject) {
        cursor.toArray(function(err, docs) {
            if (err) reject(err);
            else resolve(docs);
        });
    });
}

Now you can write your p using these helpers, put the promises for each collection in an array, and await them with Promise.all, which yields a promise right for the array of all results:

function p() {
    return connect(url).then(function(db) {
        return getArray(db.listCollections()).then(function(collections) {
            var promises = collections.map(function(collname) {
                return getArray(db.collection(collname.name).find().sort({duration_in_seconds: 1 }).limit(1));
            });
            return Promise.all(promises);
        });
    });
}

p().then(function(s) {
    console.log(s);
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thank you so much for the solution . I tried it but i get this error "TypeError: db.listCollections(...).map is not a function " – karthik006 Jun 15 '17 at 02:25
  • Oh drat, I was expecting `listCollections` to return an array (with standard `forEach` and `map` methods), not an asynchronous cursor that needs to be awaited. But we can adjust to that as well. – Bergi Jun 15 '17 at 02:37
  • Oh yea thats what I thought . It worked Thank you Bergi – karthik006 Jun 15 '17 at 02:45