Wait to resolve multiple callbacks

I'm trying to fetch IDs from one call with mongoose. Afterwards, each of these IDs is used to make another call that returns multiple objects. I am trying to fetch all of these objects.

My current attempt looks something like this:

  var members;
  var memberTimes = [];

  // Use the Group model to find a specific group
  Group.find({
    members: {
      $elemMatch: {
        $eq: req.user._id
      }
    },
    _id: req.params.group_id
  }, function(err, group) {
    if (err) {
      res.send(err);
    } else if (!group) {
      //res.send(new Error("User not in group or it does not exist"));
    }

    members = group[0].members;

    for (var member of members) {
      // Use the Time model to find a specific time
      Time.find({
        user_id: member
      }, function(err, times) {
        if (err) {
          res.send(err);
        }

        for (var time of times) {
          memberTimes.push(time);
        }
      });
    }
    //on completion of all above code, execute res.json(memberTimes);
 });

This, however, does not work because I am not waiting for all the callbacks from the Time#find. I have look at using promises but I am unsure as to how exactly make it work.

Does anyone know how this could be made to work?

Thank you, Daniel

You need to do asynchronous looping and wait for the final response. You can do something like

var members;

function getTimes(mTimes, times, i, done) {
    console.log("time " + i);
    if (i < times.length) {
      console.log(times[i]);
      mTimes.push(times[i]);
      getTimes(mTimes, times, i + 1, done);
    } else {
      done(mTimes);
    }
  }

  function asyncLoop(memberTimes, members, i, callback) {
    if (i < members.length) {
      Time.find({
        user_id: members[i]
      }, function(err, times) {
        if (err) {
          res.send(err);
        }
        console.log(times);
        getTimes(memberTimes, times, 0, function(result) {
          asyncLoop(memberTimes, members, i + 1, callback);
        });
      });
    } else {
      callback(memberTimes);
    }
  }

  // Use the Group model to find a specific group
  Group.find({
    members: {
      $elemMatch: {
        $eq: req.user._id
      }
    },
    _id: req.params.group_id
  }, function(err, group) {
    if (err) {
      res.send(err);
    } else if (!group) {
      //res.send(new Error("User not in group or it does not exist"));
    }

    members = group[0].members;

    var memberTimes = [];
    asyncLoop(memberTimes, members, 0, function(memberTimes) {
      res.json(memberTimes);
    });
  });

The above code may not run because I didn't run it but this is how you can achieve what you want.

Yes, applying promises sounds like a good idea.

You'll need to choose an implementation, on which it will depend how to promisify your find method calls. Let's assume you have a find function that takes the receiver and the options and returns you a promise - (edit) in mongoose, you're lucky and .exec() already seems to yield a promise:

function find(model, options) {
  return model.find(options).exec();
}

Then your code will look like this:

find(Group, {
  members: {$elemMatch: {$eq: req.user._id}},
  _id: req.params.group_id
}).then(function(group) {
  if (!group)
    throw new Error("User not in group or it does not exist");

  return Promise.all(group[0].members.map(function(member) {
    return find(Time, {user_id: member});
  }));
}).then(function(memberTimes) {
  res.json(memberTimes);
}, function(err) {
  res.send(err);
});

Here, Promise.all is the part where it waits until the multiple time-find-promises are resolved (in parallel).