0

I am trying to return a list of events liked by a certain user but the array returned by the code seems to always be empty (the return executes before my for loop).

router.route("/app/mylikes").get(async function(req, res, next) {
  var mylikes = [];

  const likes = await Like.find();

  likes.forEach(element => {
    bcrypt.compare(req.user.device_uuid, element.device_uuid, function(
      err,
      isMatch
    ) {
      if (isMatch) {
        mylikes.push(element.event_id);
      }
    });
  });
  res.send(mylikes);
});

the uuid of the users are hashed within the table so i'm using bcrypt to compare first then i add it to my "mylikes" array.

I'm still a noob when it comes to Promises/await/async so any help is appreciated.

CHADTO
  • 331
  • 1
  • 4
  • 12
  • 1
    I'd probably search the web for something like "javscript wait for async loop"; there are many answers here on SO and on the web in general. – Dave Newton Dec 08 '19 at 13:43
  • Does this answer your question? [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Evert Dec 08 '19 at 15:53

1 Answers1

3

You are using the callback system for bcrypt.compare, but there is also a promise interface for it when you leave out the callback argument:

for (let element of likes) {
   let isMatch = await bcrypt.compare(req.user.device_uuid, element.device_uuid);
   if (isMatch) {
      mylikes.push(element.event_id);
   }
}

Make sure to use a version of bcrypt that is compatible with your version of Node.

If for some reason you cannot move to bcrypt 3 (I would ask "why?"), then you can promisify the non-promise version of the compare method as follows:

let comparePromise = (str, hash) => 
    new Promise((resolve, reject) =>
        bcrypt.compare(str, hash, (err, isMatch) =>
            err ? reject(err) : resolve(isMatch)
        )
    );

and then:

for (let element of likes) {
   let isMatch = await comparePromise(req.user.device_uuid, element.device_uuid);
   if (isMatch) {
      mylikes.push(element.event_id);
   }
}

Note that you cannot use .forEach and await in its callback, as that would not stop the forEach iteration to suspend. That's why I have used a for...of loop.

trincot
  • 317,000
  • 35
  • 244
  • 286
  • For which line? For `bcrypt.compare`? Which version of node and bcrypt are you using? See [documentation](https://www.npmjs.com/package/bcrypt#with-promises) which even has an example that reads: `match = await bcrypt.compare(password, user.passwordHash);` – trincot Dec 08 '19 at 13:51
  • using bcrypt 2.0.2 and node 10.16.3 – CHADTO Dec 08 '19 at 13:56
  • For which line? According to the table in the bcrypt documentation, you should use bcrypt version 3+ for Node 10+ – trincot Dec 08 '19 at 13:57
  • the bcrypt.compare line – CHADTO Dec 08 '19 at 13:58
  • You have used a bcrypt version that is not guaranteed to be compatible with node 10. – trincot Dec 08 '19 at 13:59
  • @CHADTO, if you want to stick with bcrypt 2.0.2, then it's fairly simple to "promisify" the nodeback - https://stackoverflow.com/a/22519785/3478010 – Roamer-1888 Dec 08 '19 at 14:29