8

I am trying to fetch records from mongoose in loop. But it is not working as expected. I have an array of hashes with questions and answers and I'm trying to find those questions from my db. Here is my loop:

for (var i=0;i < answers.length;i++)
{
    console.log(i)
    var question_ans = eval('(' + answers[i]+ ')');

    var question_to_find = question_ans.question.toString()
    var ans = question_ans.ans.toString()
    console.log(ans)
    quiz.where("question",question_to_find).exec(function(err,results)
    {
        console.log(results)
        if (ans == "t")
        {
            user_type = results.t  
        }
        else if (ans == "f")
        {
            user_type=results.f      
        }
    })
}

and result from terminal is something like:

0
t
1
f
[ { question: 'i was here',
    _id: 5301da79e8e45c8e1e7027b0,
    __v: 0,
    f: [ 'E', 'N', 'F' ],
    t: [ 'E', 'N', 'F' ] } ]
[ { question: 'WHo r u ',
    _id: 5301c6db22618cbc1602afc3,
    __v: 0,
    f: [ 'E', 'N', 'F' ],
    t: [ 'E', 'N', 'F' ] } ]

The problem is that my questions are displaying after loop's iteration. And because of that I am unable to process them.

Please help! Regards

Gilad Green
  • 36,708
  • 7
  • 61
  • 95
nOmi
  • 297
  • 3
  • 22

2 Answers2

18

Welcome to async-land :-)

With JavaScript anything happens in parallel except your code. This means in your specific case, that the callbacks cannot be invoked before your loop has ended. You have two options:

a) Rewrite your loop from a sync for-loop to an async recurse-loop:

function asyncLoop( i, callback ) {
    if( i < answers.length ) {
        console.log(i)
        var question_ans = eval('(' + answers[i]+ ')');

        var question_to_find = question_ans.question.toString()
        var ans = question_ans.ans.toString()
        console.log(ans)
        quiz.where("question",question_to_find).exec(function(err,results)  {
            console.log(ans, results)
            if (ans == "t") {
                user_type = results.t  
            } else if (ans == "f") {
                user_type=results.f      
            }
            asyncLoop( i+1, callback );
        })
    } else {
        callback();
    }
}
asyncLoop( 0, function() {
    // put the code that should happen after the loop here
});

Additionally I recommend the study of this blog. It contains two further steps up the async-loop-stairway. Very helpful and very important.

b) Put your async function call into a closure with format

(function( ans ) {})(ans);

and provide it with the variable you want to keep (here: ans):

for (var i=0;i < answers.length;i++) {
    console.log(i)
    var question_ans = eval('(' + answers[i]+ ')');

    var question_to_find = question_ans.question.toString()
    var ans = question_ans.ans.toString()
    console.log(ans)
    (function( ans ) {
        quiz.where("question",question_to_find).exec(function(err,results)  {
            console.log(ans, results)
            if (ans == "t") {
                user_type = results.t  
            } else if (ans == "f") {
                user_type=results.f      
            }
        })
    })(ans);
}
batomaeus
  • 156
  • 1
  • 1
  • 8
heinob
  • 19,127
  • 5
  • 41
  • 61
0

you can use async and q module for find query in loop

when you run mainFunction(), the function calls getData()

const mainFunction = async()=>{
  answers
  console.log("start mainFunction")
  resultFromFindLoob  = await getData(answers)
  console.log("finish mainFunction");
  console.log("final result:" , resultFromFindLoob)
}

mainFunction();

getData() function is a promise loop function, when the function is finished, return all data and you can get final data in mainFunction()

let async = require('async');
let q = require('q');

const getData = async (answers)=>{
  console.log("start getData")
  let defer =q.defer();
  let result = []
  async.eachSeries(answers , async(asnwer)=>{
    try {
      let data = await MyModel.find(asnwer).lean();
      //do something on data (processing)
      result.push(data)
    } catch (error) {
      console.log(error)
    }
  },()=>{
    console.log("finish find loop getData")
    defer.resolve(result)
  })
  return defer.promise
}

restult of mainFunction() based on console.log

1.start mainFunction
2.start getData
3.finish find loop getData
4.finish mainFunction
5.final result : .....
Mohammad Yaser Ahmadi
  • 4,664
  • 3
  • 17
  • 39