1

I have a function that returns a Promise, that accesses the database and pulls a few lines out, assigning them to a Javascript variable.

The issue is that my '.then' clause is being triggered even though I know the Promise hasn't resolved:

app.post("/api/hashtag", function (req, res) {

FindPopularRumours().then(function (resolveVar) {
    console.log(resolveVar);
    console.log();
    res.send(resolveVar);
}).catch(function () {
    console.log("DB Error!");
    res.send("DB Error!");
});
});

And the Promise function:

function FindPopularRumours() {
return new Promise((resolve, reject) => {
    var hashtags = [];
    var dbPromise;

    db.collection(HASHTAGS).find().forEach(function (doc) {
        hashtags.push(doc.hashtag);
        console.log(hashtags);
    });
    resolve(hashtags);
});
}

The result output is:

[ ]

['#test1']

['#test1', '#test2']

['#test1', '#test2', '#test3']

As you can see, the first line ('[ ]') should ONLY be executed AFTER the hashtags have been output. But for some reason my code seems to think the Promise has been resolved before it actually has.

EDIT1

As per Ankit's suggestion, I have amended my function to:

function FindPopularRumours() {
return new Promise((resolve, reject) => {
    var hashtags = [];

    db.collection(HASHTAGS).find({}, function (err, doc) {
        if (!err) {
            doc.forEach(function (arg) {
                hashtags.push(arg.hashtag);
                console.log(hashtags);
            });
            resolve(hashtags);
        } else {
            return reject(err);
        }
    });
});
}

This still returns the same output response as before (e.g the 'then' clause is running before the promise itself).

My POST function is still the same as before.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
sparkhead95
  • 395
  • 4
  • 14
  • 1
    Of course, because you indeed resolve promise before it loaded data. `db.collection(HASHTAGS).find()` is asynchronous. – dfsq Apr 17 '17 at 11:29

2 Answers2

1

The db.collection.find() function is async, so you have to resolve the promise inside the callback for that, something like

function FindPopularRumours() {
    return db.collection(HASHTAGS).find().toArray().then( (items) => {
        return items.map( doc => doc.hashtag);
    });
}

takes advantage of the Mongo toArray() method, that returns a promise directly

adeneo
  • 312,895
  • 29
  • 395
  • 388
  • Hi, Thanks for your response. After changing to your suggestion, the console output is now: `Promise { }` This is when i do: `console.log(FindPopularRumours());` I've not seen this pending message before, but I guess the db find is not being resolved somehow..? – sparkhead95 Apr 17 '17 at 12:01
  • Uh, that's because that function is returning a **promise**, were'nt you suppose to be doing `FindPopularRumours().then(function (resolveVar) {...` – adeneo Apr 17 '17 at 12:25
0

Please note that db.collection(HASHTAGS).find() is an asynchronous call. So, your promise is resolved before database query returns. To solve this problem, you need to re-write your database query as follows:

function FindPopularRumours() {
    return new Promise((resolve, reject) => {
       var hashtags = [];
       var dbPromise;

       db.collection(HASHTAGS).find({}, function(err, doc){
           if(!err){
              doc.forEach(function (arg) {
                  hashtags.push(arg.hashtag);
                  console.log(hashtags);
              });
              resolve(hashtags);
           }else{
               return reject(err);
           }
       });
   });
}

Hope the answer helps you!

Ankit Gomkale
  • 704
  • 8
  • 16
  • Hi, thanks for your response! I have amended my code to match yours, and still have the same issue. I guess it's still executing resolve() too fast (as you mentioned, because it is async). I tried making an array of promises, and my response to be triggered only when all the promises are fulfilled, but this doesn't work either. – sparkhead95 Apr 17 '17 at 11:55
  • That's really strange! I don't see why it won't work. Can you update your question and post your amended code? – Ankit Gomkale Apr 17 '17 at 12:02
  • `doc.forEach` is asynchronous in nature hence the `resolve(hashtags)` gets executed first. To work around this use `doc.toArray` and call resolve inside the callback of toArray. – nurulnabi Apr 17 '17 at 12:20
  • Thanks for both your help (@nurulnabi too). toArray works. I have amended the original post to show solution. – sparkhead95 Apr 17 '17 at 12:35
  • @nurulnabi forEach() is a blocking call. You can check this [thread](http://stackoverflow.com/questions/5050265/javascript-node-js-is-array-foreach-asynchronous) to verify it. I am not sure what's going wrong here. – Ankit Gomkale Apr 17 '17 at 12:37
  • though forEach may be blocking but in case of mongodb cursor it doesn't work like that. – nurulnabi Apr 17 '17 at 13:09