Angular - 在之后返回数组之前等待forEach循环中的API调用

时间:2017-01-17 18:40:34

标签: javascript angularjs api

在Angular的异步中遇到一些麻烦。基本上,我正在循环几张牌。某两种类型的卡需要API调用,而不属于这些类型的卡则不需要任何API调用。循环遍历所有卡后,返回完成的卡片阵列,但我只返回那些不需要任何API调用的卡片。

这是我对其工作方式的快速模型:

// If color of colorCard is blue, it needs 2 API calls
// If color of colorCard is red, it needs 1 API call
// If color of colorCard is yellow, it doesn't need an API call
// Pretend for this example that colorCards has one yellow card, one blue card, and two red cards

var buildCards = function() {
  var finishedArray = [];
  var promises = [];
  colorCards.forEach(function(e, i){
    if (e.color === 'blue') {
      promises.push(firstBlueApiCall); 
      var firstBlueIdx = 0;
      promises.push(secondBlueApiCall); 
      var secondBlueIdx = 1;
    } else if (e.color === 'red') {
      promises.push(redApiCall); 
      var redIdx = 0;
    }

    // If card is blue or red, complete API calls before pushing to finishedArray
    if (promises.length > 0) {
        $q.all(promises).then(function(response) {
          if (e.color === 'blue') {
            e.firstBlueData = response[firstBlueIdx];
            e.secondBlueData = response[secondBlueIdx];
          } else if (e.color === 'red') {
            e.redData = response[redIdx];
          }
          finishedArray.push(e);
          promises = [];
        });
    // If card is yellow, push to finishedArray without making any API calls
    } else {
      finishedArray.push(e);
      promises = [];
    }
  })
  return finishedArray;
}

在此示例中,仅返回的finishedArray包含一张不需要API调用的黄牌,而不是所有四张牌。我怎样才能得到'return finishedArray'等到红/蓝卡完成他们的API调用?

2 个答案:

答案 0 :(得分:1)

buildCards功能可以简化:

var buildCards = function(colorCards) {
  //var deferred = $q.defer();
  //var finishedArray = [];
  var promises = [];
  colorCards.forEach(function(card, i){
    promises.push(promiseFunction(card));
  });
  //$q.all(promises).then(function(finishedCards) {
  //  deferred.resolve(finishedCards)
  //})
  //return deferred.promise;
  return $q.all(promises);
}

由于$q.defer方法已经返回一个承诺,因此无需使用$q.all制作承诺。此外,制造的承诺不能正确处理拒绝。如果任何$q.all承诺遭到拒绝,则$q.defer承诺将会挂起并永不解决。

这被称为Deferred Anti-Pattern,应该避免使用。

同样,可以修改promiseFunction函数以避免Deferred Anti-Pattern

var promiseFunction = function(card){
  //var deferred = $q.defer();
  var localPromises = [];

  if (card.color === 'blue') {
    localPromises.push(blueApiCall1); var firstBlueIdx = promises.length - 1;
    localPromises.push(blueApiCall2); var secondBlueIdx = promises.length - 1;
  } else if (card.color === 'red') {
    localPromises.push(redApiCall); var redIdx = promises.length - 1;
  }

  var cardPromise;
  if (localPromises.length > 0) {
      //$q.all(promises).then(function(res) {
      cardPromise = $q.all(localPromises).then(function(res) {
        if (card.color === 'blue') {
          card.firstBlueData = res[firstBlueIdx];
          card.secondBlueData = res[secondBlueIdx];
        } else if (card.color === 'red') {
          card.redData = res[redIdx];
        }
        //deferred.resolve(card);
        //RETURN value to chain
        return card;
      });
  } else {
      //deferred.resolve(card);
      cardPromise = $q.when(card);
  }
  //return deferred.promise;
  return cardPromise;
}

promise的then方法总是返回一个新的promise,它解析为返回给处理函数的值。此外,如果原始承诺被拒绝,则将跳过成功处理程序,拒绝将在链中传递给新承诺。这可以避免错误地挂起$q.defer

另请注意,如果没有承诺可以使用$q.all进行处理,则可以使用cardPromise创建$q.when

因为调用promise的.then方法会返回新派生的promise ,所以很容易创建一个promises链。可以创建任何长度的链,并且由于可以使用另一个承诺(将进一步推迟其解析)来解决承诺,因此可以在链中的任何点暂停/推迟承诺的解析。这使得实现强大的API

成为可能

- AngularJS $q Service API Reference - Chaining Promises

始终保证承诺。避免使用Deferred Anti-Pattern

答案 1 :(得分:0)

以下是我最终解决这个问题的方法:

mClusterManager.cluster()

我试图做到这一点,它的承诺一路下来,而且效果很好。希望这有助于将来遇到类似问题的人。