Q:

Async findOne() operation inside forEach Loop

I am having a hard time understanding JavaScript Promises.  I am searching on of my Mongoose models for objects that meet a certain condition and if they exist, I want to make the object into a plain JS object and add a property onto it.  
Unfortunately, I am unable to wrap my head around how I can ensure my forEach loop will run completely before my promise ends up resolving.  Please see my code. 
// Called to check whether a user has participated in a given list of challenges
participationSchema.statics.getParticipation = function(user, challenges) {
  return new Promise((resolve, reject) => {
    challengesArray = [];
    challenges.forEach((challenge) => {
      // Model#findOne() is Async--how to ensure all these complete before promise is resolved? 
      Participation.findOne({user, challenge})
      .then((res) => {
        if (res) {
          var leanObj = challenge.toObject();
          leanObj.participation = true;
          challengesArray.push(leanObj);
        }
      })
      .catch(e => reject(e));
    })
    console.log("CHALLENGES ARRAY", challengesArray); // Challenges Array empty :(
    resolve(challengesArray);
  });
}

I've looked through similar questions, but am unable to get to an answer.  Appreciate the help. 

A:

So, what is happening when you call getParticipation is that the forEach loop runs all the way and all individual promises for Participation.findOne are created but not yet resolved.  The execution doesn't wait for them to resolve and continues after the forEach, resolving the top-level promise challengesArray, which is still empty at that point.  Sometime after, the promises created in the forEach start resolving but their results are now lost. 
Also, as Bergi mentioned in the comments, nesting promises is considered an anti-pattern; promises should be chained, not nested.  
What you want is to use something like Promise.all to wait for all of your promises to finish first, then you filter out all non-existing results and finally return the array. 
participationSchema.statics.getParticipation = function(user, challenges) {
  return Promise.all(challenges.map(challenge => {
    return Participation.findOne({user, challenge}).then(result => {
      if (result) {
        var leanObj = challenge.toObject();
        leanObj.participation = true;
        return leanObj;
      }
    });
  })
  // at this point, results contains an array of `leanObject` and `undefined` depending if the `findOne` call returned anything and the code withing the `if` above was run
  .then((results) => {
    return results.filter(result => !! result) // filter out `undefined` results so we only end up with lean objects
  });
}

