如何在 Promise 中跳出循环?

7
我正在使用Bluebird库开发一种QA(问题/答案)应用程序。以下是情景:
1. 用户填写包含若干个问题的表单(例如5个问题)。 2. 一个问题可能有多个答案:“问题有多个答案”。 3. 答案在数据库中使用node.bcrypt加密。 4. 循环遍历答案时,如果用户的答案匹配,则无需继续检查该问题的答案。
因此,这是在同步操作中解决常见问题,但我在使用Promise进行异步操作时有些困惑。
以下是一个示例,我不知道如何继续进行:
  .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
    //   })
  })

在回调函数内开始循环并进行基本检查(if hash == password) break;之前,您可能需要单独获取加密答案。 - tymeJV
@tymeJV 我想我做不到,因为当我使用bcrypt对密码进行哈希时,与之前从数据库生成的值不同...这就是为什么我需要bcrypt比较函数的原因...也许我错了... - rizidoro
我没有我的bcrypt存储库,但那听起来是对的 - 回调地狱:\ - 这篇文章可能会有所帮助:https://dev59.com/z2rWa4cB1Zd3GeqP-2sx - tymeJV
3个回答

3
假设您想按顺序调用 compare,可以这样做:
.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);
    });
})

这段代码将“递归”遍历answers数组,在第一个true结果处停止。为了返回正确的答案(而不仅仅是“找到”的true)或者像 @Noseratio 的代码一样返回null(如果没有找到),你可以使用以下代码:

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

或者更详细地说,更加冗长的。
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);

1
以下解决方案(未经测试)实现了一个状态机来模拟foreach循环。当匹配被找到或没有更多的答案可以比较时,result承诺被解决。
  .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
  });

0

这可能是一个简单的解决方案:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接