Код внешнего интерфейса содержит список имен с флажками рядом с каждым именем. Цель - отправить электронное письмо всем проверенным именам. При нажатии кнопки отправки массив идентификаторов (для каждого пользователя) отправляется на мой бэкэнд.
Код бэкэнда запрашивает БД (mongo, используя mongoose odm) и находит пользователей. У меня есть несколько задач, которые мне нужно выполнить на сервере:
- Найти пользователей с предоставленным массивом идентификаторов
- Создать и отправить электронное письмо каждому пользователю
- Если электронное письмо успешно, обновите поле документа в БД, в которое было отправлено электронное письмо
- Если электронная почта не удалась, отправьте имя пользователя обратно во внешний интерфейс, чтобы уведомить «отправителя» о том, что попытка электронной почты не удалась.
Я работаю над этим кодом дольше, чем хотелось бы признать ... Вот что у меня есть в данный момент (меня беспокоит бэкэнд-код):
exports.sendEmailToUsers = function (req, res, next) {
mongoose.model('SpendingReport').find({ _id: { $in: req.body.recipientIds } }).populate('report user')
.find({ 'report.emailedReport': { $exists: false } }) // this needs to be refined for dev, new reports will have an emailedGradePost property
.then(spendingReports => {
return Bluebird.map(spendingReports, spendingReport => {
const email = new Email({ email: spendingReport.user.email, name: spendingReport.user.fullName }, {})
return email.send()
.then(() => {
spendingReport.report.update({ emailedReport: new Date() })
// I don't need anything returned if it is successful, this feels weird though, map doesn't
// seem like the correct function to use.
// return {spendingReport.report.emailedGradePost}
})
.catch(e => {
// I am catching each email's error so I know which email failed
return { error: e, user: spendingReport.user.fullName }
});
});
})
.then(unsuccessfulAttempts => {
// the array has the error obect from the .catch and also undefined values for the successful attempts
console.log(unsuccessfulAttempts);
})
.then(() => {
res.sendStatus(200); // filler status for now
})
.catch(e => {
console.log(e);
});
};
Вот мои вопросы:
- Я использую
Bluebird.map
, это похоже на запах кода. Теоретически, я мог бы просто использовать .map
в массиве spendingReports
, который содержит массив документов из БД, и создать электронное письмо с информацией от каждого spendingReport
. Проблема в том, что я потеряю доступ к объекту spendingReport
при возврате электронных писем следующему .then()
в цепочке обещаний, например,
exports.sendEmailToUsers = function (req, res, next) {
mongoose.model('SpendingReport').find({ _id: { $in: req.body.recipientIds } }).populate('report user')
.find({ 'report.emailedReport': { $exists: false } }) // this needs to be refined for dev, new reports will have an emailedGradePost property
.then(spendingReports => {
return spendingReports.map(spendingReport => new Email({ email: spendingReport.user.email, name: spendingReport.user.fullName }, {}));
// {email: email, spendingReport: spendingReport} I might need this format instead, referenect the note
// in the next promise chain.
})
.then(emails => {
return Bluebird.map(emails, email => {
email.send()
.then(() => {
// Note: I lost access to "spendingReport", I would need to pass this object
// with each email object {email: email, spendingReport: spendingReport}
spendingReport.report.update({ emailedReport: new Date() })
.catch(e => {
return { error: e, user: spendingReport.user.fullName };
})
})
})
})
.then(unsuccessfulAttempts => {
console.log(unsuccessfulAttempts);
})
.then(() => {
res.sendStatus(200); // filler status for now
})
.catch(e => {
console.log(e);
});
};
У меня есть вложенная цепочка обещаний (внутри Bluebird.map
электронное письмо отправляется, затем оно сохраняется в БД, чтобы оно было успешным). Я знаю, что вложенные обещания - это антишаблон. Единственный способ облегчить вложенное обещание - передать объекты документа, связанные с каждым электронным письмом, в каждом .then
, это выглядит как бремя по сравнению с тем, что вложенная цепочка обещаний в Bluebird.map
Я не знаю, что вернуть в Bluebird.map
, если электронное письмо успешно и оно успешно сохраняется. Сейчас я ничего не возвращаю, поэтому undefined
возвращается.
В идеале я мог бы отправлять все электронные письма параллельно, как Promise.all([email.send(), email.send(), email.send()])
, однако это делает сохранение в БД того, что электронная почта была успешной, более сложной (мне нужно было бы снова получить доступ к spendingReports
документы снова и обновите report
, это похоже на много запросов).