异步findOne()操作内部foreach循环



我很难理解JavaScript承诺。我正在搜索我的猫鼬模型,以了解符合特定条件的对象,如果它们存在,我想将对象放入普通的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);
  });
}

我浏览了类似的问题,但是无法找到答案。感谢帮助。

因此,当您调用getParticipation时发生了什么,forEach循环一直运行,Participation.findOne的所有个人承诺都是创建但尚未解决的。执行不等待他们解决并在forEach之后继续进行,而是解决了当时仍然是空的顶级Promise challengesArray。此后的某个时候,forEach中创建的承诺开始解决,但他们的结果现在丢失了。

此外,正如伯吉(Bergi(在评论中提到的那样,嵌套承诺被认为是反塔。应束缚,而不是嵌套。

您想要的是使用 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
  });
}

最新更新