Javascript:如何使用promise迭代数组? [英] Javascript: How to iterate on array using promises?

查看:195
本文介绍了Javascript:如何使用promise迭代数组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

实时演示 >

提供以下功能:

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

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

  return defer.promise;
}

和数字数组(例如[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');
});

以下是实现此功能的一种可能的递归版本: 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;
}

我想知道是否有更好的(也许是非递归的)方法?

解决方案

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

是的.避免使用递延反模式

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

也许是非递归的?

您可以制定一个循环,该循环链接许多then调用,但是在这里绝对可以使用递归.如果您真的想要循环,它可能看起来像这样:

function findGoodNumber(numbers) {
  return numbers.reduce(function(previousFinds, num) {
    return previousFinds.catch(function() {
      return isGood(num);
    });
  }, $q.reject());
}

然而,这效率较低,因为它总是查看所有numbers. 递归"版本将对其进行延迟评估,并且仅在当前数字不好的情况下才进行另一次迭代.

也许更快?

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

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

function findGoodNumber(numbers) {
  return Bluebird.any(numbers.map(isGood))
}

LIVE DEMO

Given the following function:

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

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

  return defer.promise;
}

and an array of numbers (e.g. [3, 9, 17, 26, 89]), I would like to find the first "good" number. I would like to be able to do this:

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

Here is one possible recursive version to implement this: 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;
}

I wonder if there is a better (maybe non-recursive) way?

解决方案

I wonder if there is a better way?

Yes. Avoid the deferred antipattern!

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

maybe non-recursive?

You can formulate a loop that chains lots of then calls, however recursion is absolutely fine here. If you really wanted the loop, it might look like this:

function findGoodNumber(numbers) {
  return numbers.reduce(function(previousFinds, num) {
    return previousFinds.catch(function() {
      return isGood(num);
    });
  }, $q.reject());
}

This is however less efficient, as it always looks at all numbers. The "recursive" version will evaluate it lazily, and only do another iteration if the current number was not good.

maybe faster?

You can fire all isGood checks in parallel, and wait for the first fulfilled to return. Depending on what isGood actually does and how well that is parallelizable, this might be "better". It potentially does a lot of unnecessary work, though; you may want to use a promise library that supports cancellation.

An example using the Bluebird library, which has a any helper function dedicated to this task:

function findGoodNumber(numbers) {
  return Bluebird.any(numbers.map(isGood))
}

这篇关于Javascript:如何使用promise迭代数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆