문제

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
    //   })
  })
도움이 되었습니까?

해결책

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);

다른 팁

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)
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top