使用 _.each 和 $q 承诺迭代小部件
Using _.each and $q promise to iterate widgets
我有一个非常简单的问题:
- 使用
_.each()
迭代一系列仪表板"widgets"。
- 调用一个函数来刷新当前小部件,并返回一个
$q
承诺。
现在,我的问题是我希望每次迭代都等待,然后再继续下一次迭代。
我的第一个版本是这个,直到我意识到我需要等待 updateWidget()
完成:
_.each(widgets, function (wid) {
if (wid.dataModelOptions.linkedParentWidget) {
updateWidget(wid, parentWidgetData);
}
});
我的第二个版本是这个,returns 一个承诺。但是,当然,我仍然有迭代继续而不等待的问题:
_.each(widgets, function (wid) {
if (wid.dataModelOptions.linkedParentWidget) {
updateWidget(wid, parentWidgetData).then(function(data){
var i = 1;
});
}
});
和 returns 一个 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;
}
对于如何在每次迭代中 "pause" 并在调用的函数完成后继续进行的想法,我将不胜感激。
谢谢,
鲍勃
******* 已更新 ************
我最新版本的迭代代码包括$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);
});
我不会尝试在您的 _.each() 中解决每个承诺,而是在您的 _.each 中构建一个承诺数组以获得如下数组:
promises = [gadgetDataService.sendAggGetRequest(url1), gadgetDataService.sendAggGetRequest(url2)....]
然后一次解决所有问题,遍历结果并设置模型:
$q.all(promises).then(function(results){ // iterate through results here })
通过链接承诺
最简单的如下:
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
});
Note: at the end, queue will resolve with the return value of the last iteration
如果你写了很多像这样的异步函数,把它包装成一个实用函数可能会有用:
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
});
这些函数在 "preprocessing" 阶段构建了一个 承诺链 ,因此您的回调会按顺序调用。还有其他方法可以做到,其中一些方法效率更高,占用内存更少,但这个解决方案是最简单的。
通过延迟迭代
此方法甚至会隐藏最后一次迭代的 return 值,并且不会预先构建完整的承诺链。缺点是,它只能用于类似对象的数组。
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 链接方法基本上在开始迭代时构建集合的快照(各个值存储在链接回调函数的闭包中),而后者则没有。
我有一个非常简单的问题:
- 使用
_.each()
迭代一系列仪表板"widgets"。 - 调用一个函数来刷新当前小部件,并返回一个
$q
承诺。
现在,我的问题是我希望每次迭代都等待,然后再继续下一次迭代。
我的第一个版本是这个,直到我意识到我需要等待 updateWidget()
完成:
_.each(widgets, function (wid) {
if (wid.dataModelOptions.linkedParentWidget) {
updateWidget(wid, parentWidgetData);
}
});
我的第二个版本是这个,returns 一个承诺。但是,当然,我仍然有迭代继续而不等待的问题:
_.each(widgets, function (wid) {
if (wid.dataModelOptions.linkedParentWidget) {
updateWidget(wid, parentWidgetData).then(function(data){
var i = 1;
});
}
});
和 returns 一个 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;
}
对于如何在每次迭代中 "pause" 并在调用的函数完成后继续进行的想法,我将不胜感激。
谢谢, 鲍勃
******* 已更新 ************
我最新版本的迭代代码包括$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);
});
我不会尝试在您的 _.each() 中解决每个承诺,而是在您的 _.each 中构建一个承诺数组以获得如下数组:
promises = [gadgetDataService.sendAggGetRequest(url1), gadgetDataService.sendAggGetRequest(url2)....]
然后一次解决所有问题,遍历结果并设置模型:
$q.all(promises).then(function(results){ // iterate through results here })
通过链接承诺
最简单的如下:
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
});
Note: at the end, queue will resolve with the return value of the last iteration
如果你写了很多像这样的异步函数,把它包装成一个实用函数可能会有用:
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
});
这些函数在 "preprocessing" 阶段构建了一个 承诺链 ,因此您的回调会按顺序调用。还有其他方法可以做到,其中一些方法效率更高,占用内存更少,但这个解决方案是最简单的。
通过延迟迭代
此方法甚至会隐藏最后一次迭代的 return 值,并且不会预先构建完整的承诺链。缺点是,它只能用于类似对象的数组。
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 链接方法基本上在开始迭代时构建集合的快照(各个值存储在链接回调函数的闭包中),而后者则没有。