[英]Resolving a promise in Angular Service
我们有一个服务,叫它AccountService
,它公开了一个名为getAccounts(customerId)
的方法。
在其实现中,它要做的就是触发$ http GET请求,并向调用控制器返回承诺,一旦解决,它将把返回的帐户数组放入控制器范围内。
在简化视图中,所有内容如下所示:
// The service
.factory('AccountService', ['$http', function($http) {
var _getAccounts = function(customerId) {
var request = {
'method': 'GET',
'url': 'http://localhost:8081/accounts/' + customerId
};
return $(request);
};
return {
getAccounts: _getAccounts
};
}]);
// Inside the conntroller
AccountService.getAccounts($scope.customerId)
.then(function(response) {
$scope.accounts = response.data;
});
因此,一旦诺言得以解决,则控制者范围将被填充帐户列表。
请注意,我尽可能简化了上面的代码,以使您了解我的问题所在,但实际上,它将是处理异常,观察者进行刷新等的代码。一切正常。
我的问题是,很多控制器都使用了这个AccountService,并且在所有这些外观中都使用了承诺解析,这不仅重复了所有这些样板解析器代码,而且使单元测试变得复杂,因为我必须成功地对R / test进行测试。每个控制器测试中的异常情况。
所以我的问题是:是否有一种很好的方法来解决服务中的诺言并将响应返回给控制器,而不是诺言?
请注意,我是Angular和JS的初学者,所以如果我的问题太幼稚,请保持谦虚。 我有大量的Java经验,我的想法似乎像遍地都是Java,但事实并非如此。
预先感谢您的输入
要回答您的原始问题:
有没有一种好的方法来解决服务中的承诺并将响应返回给控制器,而不是承诺?
在我看来,不,没有。 归结为异步调用的工作方式-您要么传递一个回调(该方法不返回任何内容),要么不传递一个回调,并且该方法返回一个将被通知的对象(一个Promise)。 可能有一些解决方法,但是我认为没有比这更好的方法了。
一种部分减少样板的方法是在服务中使用catch
,然后返回由它返回的承诺 。
考虑以下极其简化的示例:
angular.module('myApp')
.factory('NetworkRequests', [
function() {
var _getData = function() {
var promise = new Promise((resolve, reject) => {
var a = true,
data = ['a', 'b', 'c'];
if (a) {
resolve(data);
} else {
reject('Rejection reason: ...');
}
});
return promise.catch((error) => {
// Notify some error handling service etc.
console.log(error);
return [];
});
};
return {
getData: _getData
};
}
]);
promise变量将是您的http请求的结果。 您应该在catch函数中返回一些在控制器上下文中有意义的数据(例如,空数组)。 然后,您不必为控制器中的错误处理而烦恼:
angular.module('myApp')
.controller('DataController', ['NetworkRequests',
function(NetworkRequests) {
NetworkRequests.getData().then((data) => {
this.data = data;
});
}
]);
同样,这不能解决完整的问题,但是至少可以将错误处理部分封装在服务中。
您可以这样设计:一旦$http
完成了数据的获取,就将其存储为工厂变量(某种程度上是一个缓存),对于后续的工厂调用,您将检查缓存中是否包含此类数据。 如果是,则返回缓存数据,否则调用$http
调用。
这是代码:
.factory('AccountService', ['$http', '$q', function($http, $q) {
var cachedData = null;
var defered = $q.defer(); //create our own defered object
var _getAccounts = function(customerId) {
if (cachedData !== null) {
console.log('get from cachedData')
defered.resolve(cachedData); // resolve it so that the data is passed outside
return defered.promise; //return your own promise if cached data is found
} else {
var request = {
'method': 'GET',
'url': 'mockdata.json'
};
return $http(request).then((response) => { //return a normal $http promise if it is not.
console.log('get from $http');
cachedData = response.data;
return cachedData;
});
}
};
return {
getAccounts: _getAccounts
};
}]);
这是工作中的plnkr 。 您可以打开控制台,然后单击“ GetData”按钮。 您将看到它的日志第一次get from $http
,在随后的调用中,它的日志get from cachedData
。
一种方法是重用对象并用数据填充它。 ngResource使用它。
就像
var data = [];
function getAccounts(customerId) {
var promise = $http(...).then((response) => {
Object.assign(promise.data, response.data)
});
promise.data = [];
return promise;
};
数据可作为$scope.accounts = AccountService.getAccounts(...).data
进行绑定。 明显的缺点是有大量的卸载内容。
另一种方法是您提到的方法。 它是最常用的。 如果控制器中的WET代码存在问题,则应通过消除具有类继承的WET代码来解决,而不是更改其工作方式。
另一种方法是推荐的方法。 使用路由器和路由/状态解析器可以消除对异步加载数据的需求。 在解析器中解析的数据作为数组注入到路由模板中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.