Массовая отправка писем и отлов неудачных попыток - PullRequest
2 голосов
/ 27 апреля 2019

Код внешнего интерфейса содержит список имен с флажками рядом с каждым именем. Цель - отправить электронное письмо всем проверенным именам. При нажатии кнопки отправки массив идентификаторов (для каждого пользователя) отправляется на мой бэкэнд.

Код бэкэнда запрашивает БД (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, это похоже на много запросов).

1 Ответ

3 голосов
/ 27 апреля 2019

использование 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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...