我正在重构一些我最初没写的遗留代码,而且我遇到了异步数据加载的问题。第一次打开特定模态时,会加载表示表单对象的一堆数据。然后,函数循环遍历表单的输入,并根据需要将其填充。它看起来像这样(非常简化):
component.inputs.forEach(function(input) {
if (input.field == 'foo') {
input.cols = 5;
//etc.
}
if (input.field == 'bar') {
DataService.getBars().then(function(data){
data.forEach(function(e){
input.options.push(e.description);
});
};
}
if (input.field == 'baz') {
input.pattern = /regex/;
//etc.
}
});
return component;
问题当然是,如果我的input.field是'bar',代码将继续运行并在解析对DataService的异步调用之前命中最终返回,因此第一次打开模态时,输入.bartions输入没有填写.options。
是否有可能在继续之前让代码等待来自DataService的promise来解决,或者是否有另一种方法来处理大多数情况下函数是同步的情况,但是必须仅在异步调用中一个案例?或者我是否通过在ifs的这个大链中包含异步调用来自我射击?
答案 0 :(得分:1)
一种方法是创建一个promise并将其作为属性附加到返回的对象。
function getComponent() {
component.inputs.forEach(function(input) {
//create initial promise
var $promise = $q.when(input);
if (input.field == 'foo') {
input.cols = 5;
//etc.
}
if (input.field == 'bar') {
//chain from initial promise
$promise = $promise.then(function () {
//return promise for chaining
return getBarPromise(input);
});
}
//attach promise to input object
input.$promise = $promise;
});
var promises = [];
angular.forEach(inputs, function(input) {
promises.push(input.$promise);
});
//create composite promise
var $promise = $q.all(promises);
//final chain
$promise = $promise.then( function() {
//return component for chaining
return component;
});
//attach promise to component
component.$promise = $promise;
return component;
};
返回的component
对象最终将填入服务调用的结果。需要等待所有服务调用完成的函数可以从附加的$promise
属性链。
$scope.component = getComponent();
$scope.component.$promise.then( function (resolvedComponent) {
//open modal
}).catch( function(errorResponse) {
//log error response
});
因为调用promise的then
方法会返回一个新的派生promise,所以很容易创建一个promise链。可以创建任何长度的链,并且由于可以使用另一个承诺(将进一步推迟其解析)来解决承诺,因此可以在链中的任何点暂停/推迟承诺的解析。这使得实现强大的API成为可能。 1
答案 1 :(得分:0)
如果您希望继续使用现有代码结构并使其工作,则可能需要使用promises
。您还可以使用javascript' s map function
。注意:您需要将$q
注入您想要调用此函数的任何位置。
function getComponent() {
var deferred = $q.defer(),
deferred2 = $q.defer(),
promises = component.inputs.map(function(input)) {
if (input.field == 'foo') {
input.cols = 5;
deferred2.resolve();
}
else if (input.field == 'bar') {
DataService.getBars().then(function(data) {
data.forEach(function(e){
input.options.push(e.description);
});
deferred2.resolve();
}).catch(function(err)) {
deferred2.reject(err);
});
}
else if (input.field == 'baz') {
input.pattern = /regex/;
deferred2.resolve();
}
return deferred2.promise;
});
$q.all(promises)
.then(function() {
deferred.resolve(component);
}).catch(function(err) {
deferred.reject(err);
});
return deferred.promise;
}
对input
中的component.inputs
进行适当分析后,$q.all
块会触发,您可以返回新的component
对象。
最后,要设置component
对象,只需执行以下操作:
getComponent().then(function(result)) {
//Set component object here with result
$scope.component = result;
}).catch(function(err) {
// Handle error here
});