前端代码包含一个名称列表,每个名称旁边带有复选框。目的是让电子邮件发送到所有已检查的名称。单击提交按钮后,(每个用户的)ID数组将发送到我的后端。
后端代码查询数据库(使用mongoose odm的mongo)并找到用户。我需要在后端完成一些任务:
我从事此代码的工作时间超过了我想承认的时间……这是我目前的情况(我担心后端代码):
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);
});
};
我有一个嵌套的Promise链(在Bluebird.map
内部,已发送电子邮件,然后将其成功保存到数据库中)。我知道嵌套的承诺是一种反模式。减轻嵌套承诺的唯一方法是传递与每个.then
中的每封电子邮件相关的文档对象,与仅在Bluebird.map
中嵌套嵌套的诺言链相比,这感觉负担更大。 p>
我不知道成功发送电子邮件并成功保存后在Bluebird.map
中返回什么。目前,我什么也没返回,所以返回了undefined
。
理想情况下,我可以并行发送所有电子邮件,例如Promise.all([email.send(), email.send(), email.send()])
,但是,这使得向数据库保存电子邮件成功的难度更大(我需要再次访问{ {1}}再次进行文档编制并更新spendingReports
,感觉就像很多查询一样。
答案 0 :(得分:3)
使用async-await可以减少您的问题(因为您可以按索引获取所有项目)
async function(req, res, next) {
let spendingReports = await mongoose.model('SpendingReport').find(...)
let emails = spendingReports.map(r=>new Email(...))
let sendingmails = emails.map(e=>e.send())
let success=[],fail=[];
await Promise.all(sendingmails.map((s,i)=>s.then(_=>success.push(i)).cache(_=>fail.push(i))))
//now you have index of success and failed mails.
//just process these data and do whatever you want
}
中间数据不是必需的,就像这样的单行代码(不必这样做)
async function(req, res, next) {
let success=[],fail=[];
await Promise.all(await mongoose.model('SpendingReport').find(...).then(spendingReports => spendingReports.map(r=>(new Email(...)).send().then(_=>success.push(r)).cache(_=>fail.push(r))))
//now you have success and failed spendingReports.
//just process these data and do whatever you want
}