【问题标题】:Angular - wait on API calls in forEach loop before returning array afterwardsAngular - 在之后返回数组之前等待 forEach 循环中的 API 调用
【发布时间】:2017-01-18 08:47:12
【问题描述】:

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 调用?

【问题讨论】:

  • JavaScript 是单线程的。当函数完成时,它只能返回可用的数据或将在 future 中实现的 pending 承诺。

标签: javascript angularjs api


【解决方案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 制造一个promise,因为$q.all 方法已经返回了一个promise。此外,制造的承诺不能正确处理拒绝。如果$q.all 的任何一个promise 被拒绝,$q.defer 的promise 将挂起并且永远不会解决。

这被称为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,它解析为返回给处理函数的值。此外,如果原始 Promise 被拒绝,成功处理程序将被跳过,拒绝将沿着链传递到新的 Promise。这避免了$q.defer 的错误挂起。

另请注意,在没有使用$q.all 处理的承诺的情况下,可以使用$q.when 创建cardPromise

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

-- AngularJS $q Service API Reference - Chaining Promises

始终链接承诺。避免使用Deferred Anti-Pattern

【讨论】:

    【解决方案2】:

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

    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;
      }
    
      if (localPromises.length > 0) {
          $q.all(promises).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);
          });
      } else {
        deferred.resolve(card);
      }
      return deferred.promise;
    }
    
    var buildCards = function() {
      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;
    }
    

    我试图做到这一点,所以它一直是承诺,而且效果很好。希望这对以后遇到类似问题的人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-09
      • 1970-01-01
      • 2011-01-06
      • 1970-01-01
      • 2020-10-25
      • 2016-05-08
      • 1970-01-01
      相关资源
      最近更新 更多