4

I'm pretty new to this type of programming and I'm having some trouble populating an array from a nested call. I'm pretty sure this needs to be done using callbacks, but I'm having trouble wrapping my brain around it. Closures must also come into play here. I tried searching the web for a similar example but didn't find much.

Here is my original code. I tried a few different approaches but didn't pull it off.

TaskSchema.statics.formatAssignee = function(assignees) {
  var users = [];

  assignees.forEach(function(uid) {
    mongoose.model('User').findById(uid, function(err, user) {
      users.push({
          name: user.name.full
        , id: user.id
      });
    });
  });

  return users;
}

3 Answers 3

6

I really like the following pattern (recursion is the most elegant solution to async loops):

TaskSchema.statics.formatAssignee = function(assignees, callback) {
  var acc = []
    , uids = assignees.slice()
  (function next(){
    if (!uids.length) return callback(null, acc);

    var uid = uids.pop()
    mongoose.model('User').findById(uid, function(err, user) {
      if (err) return callback(err);
      acc.push({
        name: user.name.full
      , id: user.id
      });
      next();
    });
  })();
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks Adrien, this did the trick. I ended up using this with the async library mentioned below to work with the array. I suppose I can replace this recursive function with with a call to async.map, but at least I now know how to solve this problem without depending on a library.
2

Check out async, it has an async foreach loop.

Edit

Here is the foreach method from the async library

async.forEach = function (arr, iterator, callback) {
    if (!arr.length) {
        return callback();
    }
    var completed = 0;
    _forEach(arr, function (x) {
        iterator(x, function (err) {
            if (err) {
                callback(err);
                callback = function () {};
            }
            else {
                completed += 1;
                if (completed === arr.length) {
                    callback();
                }
            }
        });
    });
};
var _forEach = function (arr, iterator) {
    if (arr.forEach) {
        return arr.forEach(iterator);
    }
    for (var i = 0; i < arr.length; i += 1) {
        iterator(arr[i], i, arr);
    }
};

3 Comments

If you look at the source, you'll see basically the same pattern of: do stuff, increment counter, if counter==input.length then invoke callback I have mentioned. Basically a choice of DIY or library, but the idea is the same.
@z5h Right, it becomes a recursive loop.
Hey Chance, I checked out async and it looks very useful. Thanks for the recommendation.
0

you could do something like:
Give formatAssignee a callback.
Count down how many users you need to push onto users.
After you push the last one, invoke the callback with the parameter users.

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.