0

I have to fill my risultato json with some information taken by MongoDB with a mongoose query, so at the end of the loop i have to return risultato with an hapijs controller. If i use a return at the end i have an error like this:

Error:reply interface called twice

And if i use the return out of the loop it return:

var risultato = {
"models": [],
}

This is my code:

Myfunction: function(request, reply) {

  var risultato = {
    "models": [],
  }

  for(var i=0; i<3; i++){
    Collections                 //this is a mongoose model
      .findOne()
      .where({_id: id]})
      .populate('models')
      .exec( function(err, result) {
         risultato.models.push.apply(risultato, result.models);
         console.log(risultato)
         return reply(risultato)

      });
  }
};

What can i do?

gnerkus
  • 11,357
  • 6
  • 47
  • 71

2 Answers2

2

You're running asynchronous queries in a for loop, each of which calls the reply() method in their handler.

You need to use an asynchronous loop, or a system where you can judge whether all three callbacks have been called before returning.

Myfunction: function(request, reply) {

var risultato = {
"models": [],
}

for(var i=0; i<3; i++){
Collections                 //this is a mongoose model
  .findOne()
  .where({_id: id]})
  .populate('models')
  .exec( function(err, result) {


       risultato.models.push.apply(risultato, result.models);
       console.log(risultato)

       if (resultato.models.length === 3) //All callbacks have returned
       {
            reply(risultato);
       }

    });
}
}

The best way would be to use the async library. Start using this early on and you won't have a problem later.

With the Async module, the same query can be executed as follows:

var async = require('async');

Myfunction: function(request, reply) {

  var queries = [];

  for(var i=0; i<3; i++) {

    queries.push(function(callback){

      Collections                 //this is a mongoose model
      .findOne()
      .where({_id: id]})
      .populate('models')
      .exec( function(err, result) {
         console.log(risultato)
         callback(err, result);
      });
    });

  }

  async.parallel(queries, function (err, result) {

    reply({models: result});
  })
};
ZeMoon
  • 20,054
  • 5
  • 57
  • 98
1

You can achieve it through promiseFor wrapped by BlueBird(one promise library).

var Promise = require('bluebird');

var promiseFor = Promise.method(function(condition, action, value) {
    if (!condition(value)) return value;
    return action(value).then(promiseFor.bind(null, condition, action));
});

Myfunction: function(request, reply) {
    var risultato = {
        "models": [],
    }

    promiseFor(function(count) {
         return count < 3;
    }, function(count) {
         return Collections 
            .findOne()
            .where({_id: id]})
            .populate('models')
            .exec( function(err, result) {
                risultato.models.push.apply(risultato, result.models);
                console.log(risultato)
                return ++count;
             });
   }, 0).then(function() {
       reply(risultato);
   });

}
Community
  • 1
  • 1
zangw
  • 43,869
  • 19
  • 177
  • 214