1

I am using mongodb find query inside loop as I need to run the find query 5 times. And I used below code for that:

let result = {};
let miles = ['5','10','15','25'];

let i = 0;
while (i < miles.length) {
  Shops.find({ 'shopInfo.address':{ $geoWithin:{ $centerSphere: [ [ 75.83183541365247, 30.902146005639267 ], miles[i] / 3959 ] } } }).then(response=>{
      if(i==4){
        result[miles[i]] = response.length;            
        res.json(result);
      }else{
        result[miles[i]] = response.length;            
        i++;
      }
  })
  .catch(err=>{
    console.log(err)
  }); 
}

And when I hit the api on browser. It's not returning with anything and getting below error in console:

error image

Please help me, How can I solve the issue?

2
  • Is it working without the while loop? Commented Apr 26, 2019 at 6:37
  • yes, If I use it out of loop to get only one result then it's working fine. Commented Apr 26, 2019 at 6:39

4 Answers 4

2

The following happens:

Your while loop runs and starts an async action. However as that async action is not finished yet, the i++ will not be executed, therefore the loop runs forever, creates more and more async actions that fill up your memory, and finally NodeJS crashes because it runs out of memory. To prevent that, you should not synchronously iterate with an asynchronous task. Either await the asynchronous task inside a loop, or .map the miles to the asynchronous tasks then await them all:

 const entries = Promise.all(miles.map(mile =>        
    Shops.find({ 'shopInfo.address':{ $geoWithin:{ $centerSphere: [ [ 75.83183541365247, 30.902146005639267 ], mile / 3959 ] } } })
      .then(entry => ([mile, entry.length]))
 ));

 entries.then(entries => {
   const result = Object.fromEntries(entries);
   //...
});

 // Polyfill: Object.fromEntries
 if(!Object.fromEntries)
  Object.defineProperty(Object, "fromEntries", {
    value(entries) {
      const result = {};
      for(const [k, v] of entries)
        result[k] = v;
       return result;
     }
  });
Sign up to request clarification or add additional context in comments.

5 Comments

@rajat no, Shops.find is asynchronous.
No, not really. Wether a loop is syncrhonous or not depends on how you use it.
@rajat oh and by the way, mongodb has a .count() method (not sure how that is named in mongoose (?)) which will greatly speed up your code.
Do you have Node 12.0.0 ?
I added a polyfill.
0

Normal loops have issues with looping over async code. You can implement an asyncForEach helper function:

async function asyncForEach(array, callback) {
  if (array)
    for (let index = 0; index < array.length; index++) {
      await callback(array[index], index, array);
    }
}

And call it executing your mongoQueries:

await asyncForEach(miles, async mile => {
   const response = await Shops.find({ 'shopInfo.address':{ $geoWithin:{ $centerSphere: [ [ 75.83183541365247, 30.902146005639267 ], miles[i] / 3959 ] } } })
   // do soming with the response 
})

4 Comments

Should I need to apply above both functions?
the first code blocks declares the helper function asyncForEach and second code block shows how to use the helper function
I use this helper function several time in my project and refactored it to a util functions class
for(const mile of miles) { await Shops.find(...) } why do you need a helper for that?
0

Use async/await to wait mongo promise get resolved and then increment variable. Try this :

async function test() {
  let result = {};
  let miles = ["5", "10", "15", "25"];

  let i = 0;
  while (i < miles.length) {
    try {
      let response = await Shops.find({
        "shopInfo.address": {
          $geoWithin: {
            $centerSphere: [
              [75.83183541365247, 30.902146005639267],
              miles[i] / 3959
            ]
          }
        }
      });
      if (i == 4) {
        result[miles[i]] = response.length;
        res.json(result);
      } else {
        result[miles[i]] = response.length;
        i++;
      }
    } catch (err) {
      console.log(err);
    }
  }
}

1 Comment

Thanks for your answer. But neither it gives me error not it gives me response
0

Below is the code which worked for me:

let miles = ['5','10','15','25','50'];
async.map(miles,getDistance , function(err, results) {
   if(err){
        console.log(err);
   } 
   res.json(results);
});

function getDistance(mile, callback) {
  Shops.count({ 'shopInfo.address':{ $geoWithin:{ $centerSphere: [ [ 75.83183541365247, 30.902146005639267 ], mile / 3959 ] } } }).then(response=>{
    if(response){
      callback(null,response);
    }
  })
}

I Hope It will work for you as well.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.