forEach Loop中的异步findOne()操作

时间:2017-06-02 03:36:36

标签: javascript node.js asynchronous mongoose promise

我很难理解JavaScript Promises。我在我的Mongoose模型中搜索满足某个条件的对象,如果它们存在,我想将对象变成一个普通的JS对象并在其上添加一个属性。

不幸的是,在我的承诺最终解决之前,我无法确定如何确保我的forEach循环完全运行。请看我的代码。

// 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);
  });
}

我查看了类似的问题,但无法得到答案。感谢帮助。

1 个答案:

答案 0 :(得分:2)

因此,当您致电getParticipation时发生的事情是forEach循环一直在运行,Participation.findOne的所有个别承诺都是已创建但尚未解决。执行不会等待他们解决并在forEach之后继续,解决顶级承诺challengesArray,该承诺在此时仍为空。之后,forEach中创建的承诺开始解析,但结果现在已丢失。

另外,正如Bergi在评论中提到的,嵌套承诺被认为是anti-pattern;承诺应该链接,而不是嵌套。

你想要的是使用类似Promise.all之类的东西来等待你的所有承诺先完成,然后你过滤掉所有不存在的结果,最后返回数组。

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
  });
}