Javascript:如何使用promises迭代数组?

Mis*_*hko 11 javascript promise angularjs q angular-promise

LIVE DEMO

鉴于以下功能:

function isGood(number) {
  var defer = $q.defer();

  $timeout(function() {
    if (<some condition on number>) {
      defer.resolve();
    } else {
      defer.reject();
    }
  }, 100);

  return defer.promise;
}
Run Code Online (Sandbox Code Playgroud)

和一组数字(例如[3, 9, 17, 26, 89]),我想找到第一个 "好"的数字.我希望能够做到这一点:

var arr = [3, 9, 17, 26, 89];

findGoodNumber(arr).then(function(goodNumber) {
  console.log('Good number found: ' + goodNumber);
}, function() {
  console.log('No good numbers found');
});
Run Code Online (Sandbox Code Playgroud)

这是一个可能的递归版本来实现这个: DEMO

function findGoodNumber(numbers) {
  var defer = $q.defer();

  if (numbers.length === 0) {
    defer.reject();
  } else {
    var num = numbers.shift();

    isGood(num).then(function() {
      defer.resolve(num);
    }, function() {
      findGoodNumber(numbers).then(defer.resolve, defer.reject)
    });
  }

  return defer.promise;
}
Run Code Online (Sandbox Code Playgroud)

我想知道是否有更好的(可能是非递归的)方式?

Ber*_*rgi 9

我想知道是否有更好的方法?

是.避免延迟反模式!

function isGood(number) {
  return $timeout(function() {
    if (<some condition on number>) {
      return number; // Resolve with the number, simplifies code below
    } else {
      throw new Error("…");
    }
  }, 100);
}
function findGoodNumber(numbers) {
  if (numbers.length === 0) {
    return $q.reject();
  } else {
    return isGood(numbers.shift()).catch(function() {
      return findGoodNumber(numbers);
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

也许不是递归的?

你可以制定一个链接大量then调用的循环,但是递归在这里绝对没问题.如果你真的想要循环,它可能看起来像这样:

function findGoodNumber(numbers) {
  return numbers.reduce(function(previousFinds, num) {
    return previousFinds.catch(function() {
      return isGood(num);
    });
  }, $q.reject());
}
Run Code Online (Sandbox Code Playgroud)

然而,这一点效率较低,因为它总是可以看到numbers."递归"版本将懒惰地评估它,并且如果当前数字不好则仅进行另一次迭代.

也许更快?

您可以isGood并行触发所有检查,并等待第一个完成检查返回.根据isGood实际做的和可并行化的程度,这可能会"更好".但是,它可能会做很多不必要的工作; 您可能想要使用支持取消的promise库.

使用Bluebird库的示例,该库具有专用于此任务的any辅助函数:

function findGoodNumber(numbers) {
  return Bluebird.any(numbers.map(isGood))
}
Run Code Online (Sandbox Code Playgroud)