Мне трудно понять обещания JavaScript. Я ищу свои модели 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);
});
}
Я просмотрел похожие вопросы, но не могу ответить. Цените помощь.
Итак, что происходит, когда вы вызываете getParticipation
это то, что цикл forEach
выполняется полностью, и все индивидуальные обещания для Participation.findOne
созданы, но еще не решены. Выполнение не дожидается их разрешения и продолжается после forEach
, разрешая обещание верхнего уровня challengesArray
, которое на данный момент еще пусто. Спустя некоторое время обещания, созданные в forEach
начинают разрешаться, но их результаты теряются.
Кроме того, как отметил Берги в комментариях, вложение обещаний считается анти-паттерном; обещания должны быть прикованы, а не вложены.
Вам нужно использовать что-то вроде Promise.all
чтобы Promise.all
дождаться 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
});
}
promises.all()
гарантирует, что они все завершены, правильно? Пожалуйста, дайте мне попробовать это очень быстро и вернемся к вам.Participation.findOne
и все запросы выполняются, в основном, параллельно. Затем эти запросы начинают выполняться в некотором произвольном порядке, и обещания начинают разрешаться соответствующим образом, который запускает условиеthen
для каждого из них и проверяет, есть ли результаты, преобразуя их вleanObj
если это так. Как только все эти обещания разрешены,Promise.all
разрешается с массивом результатов, полученных по каждому обещанию.