1

I'm trying to fill outputArray and then send it as a JSON. When I add console.log() after outputArray.push I can see that outputArray is being filled. But in the bottom function, it prints an empty array. I see that the bottom function works before the array is filled. How can I modify below code so that I can receive the full array. Thanks in advance.

      var outputArray = [];

      async.each(generalArr, function(item, callback)
      {
          var docs =  collection.find({ id: { "$in": item}}).toArray();


        docs.then(function(singleDoc)
        {
          if(singleDoc)
          {


              outputArray.push.apply(outputArray, singleDoc);
          }
        });
        callback(null, outputArray);

    }, function(err,results)
      {
      if (err) 
        return console.log('ERROR', err);

      console.log(results);
      res.json(results);


      }
  ) 
jason
  • 6,962
  • 36
  • 117
  • 198
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – str Jul 25 '17 at 09:08
  • @str none of the answers are about Node.js. And they are not related with this question. – jason Jul 25 '17 at 09:10
  • The same concepts also apply to Node.js (it is JavaScript after all). You seem to have nested async functions of which the inner changes data but you expect it to be available in the outer. If this is what you want, then yes the linked question is related. – str Jul 25 '17 at 09:54
  • @str I canceled the first async. But still I can't get the value of outputArray out of the loop.. And I really couldn't find a solution in the linked question. :( – jason Jul 25 '17 at 11:09
  • Then please update (and properly indent) your code. – str Jul 25 '17 at 11:10
  • @str I updated the code. Thanks. – jason Jul 25 '17 at 11:18
  • `each` doesn't have a result. Use `map` instead. Or, since you're working with promises, don't use async.js at all! That said, you need to move the `callback(…)` call into the asynchronous `then` callback. – Bergi Jul 25 '17 at 11:22
  • @Bergi It would be great if you post a simple example. Thanks. – jason Jul 25 '17 at 11:25

2 Answers2

8

There are a couple of mistakes in the code.

  1. You are calling callback(null, outputArray) in the wrong place. It should be right after the innermost if condition
  2. async.each final callback takes only one argument - err, so results is not available

The following code should work...

var outputArray = [];

async.each(generalArr, function(item, callback) {
    var docs =  collection.find({ id: { "$in": item}}).toArray();
    docs.then(function(singleDoc) {
        if(singleDoc) {
            outputArray.push.apply(outputArray, singleDoc);
        }
        callback();
    });
}, function(err) {
    if (err) return console.log('ERROR', err);

    console.log(outputArray);
    res.json(outputArray);
});

I'm assuming that catch code is also there for the DB promise query, else the final callback might never be called.

Mahesh J
  • 510
  • 3
  • 10
  • Thank you for your answer. Could you explain why `callback` should be right after the innermost `if`? – jason Jul 25 '17 at 11:40
  • @jason This has been explained in all detail in the duplicate question: https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call Please read it. – str Jul 25 '17 at 11:53
  • 1
    ok, because `docs.then` is also asynchronous. By the time `callback` is called (if it's in the parent block like you have now), `outputArray` might not have been populated and you might get incorrect results in the final async callback. So you have to move it to the innermost block so that your code waits until the results are back from the DB. – Mahesh J Jul 25 '17 at 11:55
6

Don't use async.js when plain promises are so much easier:

Promise.all(generalArr.map(function(item) {
  return collection.find({id: {"$in": item}}).toArray();
})).then(function(outputArray) {
  var results = outputArray.filter(Boolean);
  console.log(results);
  res.json(results);
}, function(err) {
  console.log('ERROR', err);
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375