Domanda

I'm doing kind of QA (Question/Answers) application using the Bluebird library. So here's the scenario:

  1. User fill the form with answers for some number of questions (e.g 5 questions).
  2. A question has more than 1 possible answer: "Question has many answers"
  3. Answers are encrypted (bcrypt) on database using node.bcrypt
  4. When looping through answers, if user answer matches, there's no need to continue checking the answer for that question.

So it's a common problem to solve when doing things synchronous, but I'm a little lost to do that async with promises.

Here's a sample of what I don't know how to proceed:

  .then(function(answers) {
    var compare = Promise.promisify(bcrypt.compare);
    // foreach answer, I need to check like this
    // compare(answer.password, user.password).then(function(match){
    //      if (match) break; <-- something like this
    //   })
  })
È stato utile?

Soluzione

Assuming you want to call the compares sequentially, this will do it:

.then(function(answers) {
    var compare = Promise.promisify(bcrypt.compare),
        i = 0;
    return Q(false).then(function checkNext(res) {
        return res ||
               i<answers.length && compare(answers[i++].password, user.password)
                                     .then(checkNext);
    });
})

It will "recursively" step trough the answers array, stopping on the first true result. To return the correct answer (instead of just true for "found") or null (if not found) like @Noseratio's code, you could use

    var i = 0, answer;
    return Q(false).then(function checkNext(res) {
        return res ? answer : (i<answers.length || null) && compare((answer = answers[i++]).password, user.password).then(checkNext);
    });

or better the more verbose

function next(i) {
    if (i < answers.length)
        return compare(answers[i].password, user.password).then(function(res) {
             return res ? answers[i] : next(i+1);
        });
    else
        return null;
}
return next(0);

Altri suggerimenti

The following solution (untested) implements a state machine to simulate foreach loop. The result promise is resolved when the match has been found, or when there is no more answers to compare:

  .then(function(answers) {
    var result = new Promise();
    var i = 0;
    function nextStep() {
      if (i >= answer.length)
        result.resolve(null);
      else {
        var answer = answers[i];
        if (compare(answer.password, user.password).then(function(match) {
          if (match)
            result.resolve(answer);
          else {
            i++;
            nextStep(); // do the next step
          }
        })
      }
    }
    process.nextTick(nextStep); // do the first step asynchronously    
    return result; // return the result as a promise
  });

It could be a simple solution:

let breakLoop = false
for (let answer of arr) {
  if (breakLoop) continue
  compare(answer.password, user.password)
    .then(match => {
      if (match) breakLoop = true
    })
    .catch(err => breakLoop = true)
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top