使用_.each和$ q承诺迭代小部件

时间:2015-12-08 20:20:28

标签: angularjs angular-promise

我有一个非常直截了当的问题,我在这里:

  1. 通过一系列仪表板"小部件"迭代使用_.each()
  2. 调用函数刷新当前窗口小部件,并返回$q版本。
  3. 现在,我的问题是我希望在继续下一次迭代之前,每次迭代都要等待。

    我的第一个版本是这个,直到我意识到我需要等待updateWidget()完成:

    
    
    _.each(widgets, function (wid) {
      if (wid.dataModelOptions.linkedParentWidget) {
          updateWidget(wid, parentWidgetData);
      }
    });
    
    
    

    我的第二个版本就是这个版本,它会返回一个承诺。但是,当然,我仍然遇到迭代继续而不等待的问题:

    
    
    _.each(widgets, function (wid) {
      if (wid.dataModelOptions.linkedParentWidget) {
        updateWidget(wid, parentWidgetData).then(function(data){
          var i = 1;
        });
      }
    });
    
    
    

    和被调用的函数,它返回一个deferred.promise对象(然后对小部件数据进行服务调用):

    
    
    function updateWidget(widget, parWidData) {
        var deferred = $q.defer();
    
        // SAVE THIS WIDGET TO BE REFRESHED FOR THE then() SECTION BELOW 
        $rootScope.refreshingWidget = widget;
    
        // .. SOME OTHER VAR INITIALIZATION HERE...       
        
        var url = gadgetDataService.prepareAggregationRequest(cubeVectors, aggrFunc, typeName, orderBy, numOrderBy, top, filterExpr, having, drillDown);
        
        return gadgetDataService.sendAggGetRequest(url).then(function (data) {
    	var data = data.data[0];
    	var widget = {};
    	if ($rootScope.refreshingWidget) {       
    	    widget = $rootScope.refreshingWidget;
    	}
    	// BUILD KENDO CHART OPTIONS
    	var chartOptions = chartsOptionsService.buildKendoChartOptions(data, widget);                
    
    	// create neOptions object, then use jquery extend()
    	var newOptions = {};
    	$.extend(newOptions, widget.dataModelOptions, chartOptions);
    	widget.dataModelOptions = newOptions;
    
    	deferred.resolve(data);
        });
        
        return deferred.promise;
    }
    
    
    

    我很感激您关于如何"暂停"在每次迭代时,并在被调用函数完成后继续。

    谢谢你, 鲍勃

    *******更新************

    我的最新版本的迭代代码包括$q.all(),如下所示:

    
    
    // CREATE ARRAY OF PROMISES !!
    var promises = [];
    _.each(widgets, function (wid) {
      if (wid.dataModelOptions.linkedParentWidget) {
        promises.push(updateWidget(wid, parentWidgetData));
      }
    });
    $q.all(promises)
    .then(function () {
      $timeout(function () {
        // without a brief timeout, not all Kendo charts will properly refresh.
        $rootScope.$broadcast('childWidgetsRefreshed');
      }, 100);              
    });    
    
    
    

2 个答案:

答案 0 :(得分:4)

通过链接承诺

最简单的是:

var queue = $q.when();
_.each(widgets, function (wid) {
  queue = queue.then(function() {
    if (wid.dataModelOptions.linkedParentWidget) {
      return updateWidget(wid, parentWidgetData);
    }
  });
});
queue.then(function() {
  // all completed sequentially
});
  

注意:最后,队列将使用最后一次迭代的返回值解析

如果您编写了许多这样的异步函数,将它包装到实用程序函数中可能会很有用:

function eachAsync(collection, cbAsync) {
  var queue = $q.when();
  _.each(collection, function(item, index) {
    queue = queue.then(function() {
      return cbAsync(item, index);
    });
  });
  return queue;
}

// ...
eachAsync(widgets, function(wid) {
  if (wid.dataModelOptions.linkedParentWidget) {
    return updateWidget(wid, parentWidgetData);
  }
}).then(function() {
  // all widgets updated sequentially
  // still resolved with the last iteration
});

这些函数在“预处理”阶段构建 promises链,因此您的回调会按顺序调用。还有其他方法可以做到这一点,其中一些方法效率更高,内存更少,但这个解决方案最简单。

延迟迭代

此方法将隐藏最后一次迭代的返回值,并且不会在beforehands之前构建完整的promise链。缺点是,它只能用于像对象这样的数组。

function eachAsync(array, cbAsync) {
  var index = 0;
  function next() {
    var current = index++;
    if (current < array.length) {
      return $q.when(cbAsync(array[current], current), next);
    }
    // else return undefined
  }
  // This will delay the first iteration as well, and will transform
  // thrown synchronous errors of the first iteration to rejection.
  return $q.when(null, next); 
}

这将迭代任何可迭代的

function eachAsync(iterable, cbAsync) {
  var iterator = iterable[Symbol.iterator]();
  function next() {
    var iteration = iterator.next();
    if (!iteration.done) {
      // we do not know the index!
      return $q.when(cbAsync(iteration.value), next);
    } else {
      // the .value of the last iteration treated as final
      // return value
      return iteration.value;
    }
  }
  // This will delay the first iteration as well, and will transform
  // thrown synchronous errors of the first iteration to rejection.
  return $q.when(null, next); 
}

请记住,当集合在迭代期间发生更改时,这些方法的行为会有所不同。 promise链接方法基本上在集合开始迭代时构建集合的快照(各个值存储在链式回调函数的闭包中),而后者则没有。

答案 1 :(得分:1)

我没有尝试解析_.each()中的每个promise,而是在_.each中构建一个promises数组来获取类似的数组:

promises = [gadgetDataService.sendAggGetRequest(url1), gadgetDataService.sendAggGetRequest(url2)....]

然后立即解决所有问题,遍历结果并设置模型:

$q.all(promises).then(function(results){ // iterate through results here })