0

I'm working with Promises in Node (also using Mongoose and Lodash) and I need to wait for a list of operations against the database to be ready and push the results to an array:

var users = [];

var email_to_id = function(email) {
  return new Promise(function(resolve, reject) {
    User.findOne({email: email}).exec().then(function(user, err) {
      if (user) {
        resolve(user);
      } else {
        reject(err);
      }
    });
  });
};

_.each(emails, function(email) {
  users.push(
    email_to_id(email).then(function(user, err) {
      if (user) {
        return user;
      } else {
        // How not to return anything?
      }
    }).catch(next)
  );
});

So far so good, however, if I pass a wrong email and email_to_id rejects, the _.each function will push a null to the users array.

How to prevent it from pushing null? And instead not push anything.

trs
  • 863
  • 2
  • 16
  • 35
  • `.catch(next)` This looks important, what does `next` do? – CertainPerformance Oct 24 '18 at 06:26
  • 1
    Also, I'm *pretty sure* `.then` will ignore its second argument - won't `err` always be `undefined`? (that's what the `.catch` is for) – CertainPerformance Oct 24 '18 at 06:38
  • `.then(function(user, err) {` that's not a promise - you're also using the [Promise constructor anti-pattern](https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it), as `User.findOne({email: email}).exec()` returns a promise, there is no need to wrap it in a new promise – Bravo Oct 24 '18 at 07:56
  • Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi Oct 24 '18 at 08:03
  • Don't use `_.each` with promises! Use `map`, and then `Promise.all` – Bergi Oct 24 '18 at 08:04

1 Answers1

1

You could try this.

const reflect = p => p.then(v => ({data: v, status: "fulfilled" }),
                            e => ({data: e, status: "rejected" }));
const emailPromises = emails.map((email)=>email_to_id(email)).map(reflect);

const filterdSuccessPromises = Promise.all(emailPromises)
                                        .then(results=>results
                                                            .filter(x => x.status === "resolved")
                                                            .map(data=>data.data)
                                        ));

Based on this answer. It maps all values into objects which says whether it succeeded or not. Then we filter values which succeeded. And you can get the data by using filterdSuccessPromises.then

EDIT: Thinking about this you should actually filter/clean invalid emails before you make any calls less expensive, I am not sure why i didn't suggest this first

var emails = emails.filter((email)=>email) //or any other validations.
Shyam Babu
  • 1,069
  • 7
  • 14