这是一个简单的问题,但由于承诺的异步性质,似乎很棘手。
在我的数据服务中,我想确保在继续下一步之前从服务器检索数据,因为其他功能/客户端依赖于该数据。
我不希望客户端注册回调或使用.then(),并且在请求数据时肯定不会使用$ timeout。
如何确保依赖于我的数据服务的控制器和服务在请求时立即获取数据?我用下面的代码解释了我的问题。如果可能的话,我非常希望继续我目前的做法。
AngularJS版本:1.4 +
我当前的方法
//Data Service
App.factory('HealthyFoodService', function($resource, $q) {
var fruitsPromise = $resource('api/food/fruits', {}).query().$promise;
var veggiesPromise = $resource('api/food/veggies',{}).query().$promise;
var fruitsData, veggiesData;
//Ensure $q populates the data before moving on to next statement.
$q.all([fruitsPromise, veggiesPromise]).then(function(data) {
fruitsData = data[0];
veggiesData = data[1];
}
function getCitrusFruits() {
var citrusFruits;
var allFrutis = fruitsData;
//code breaks here because fruitsData is still undefined when called from a controller or another service.
//some logic for this function
return citrusFruits;
}
function getLeafyVeggies() {
var leafyVeggies;
var allVeggies = veggiesData;
//code breaks here because veggieData is still undefined when called from a controller or another service.
//some logic for this function
return leafyVeggies;
}
function getLeafyVeggyByName(name) {
//this function is called from other services and controllers.
var leafyVeggies = getLeafyVeggies();
return leafyVeggies[name];
}
return {
getCitrusFruits: getCitrusFrutis,
getLeafyVeggies: getLeafyVeggies,
getLeafyVeggyByName: getLeafyVeggyByName
});
以下是两个客户。一个是控制器,另一个是服务。他们都需要立即获取数据,因为以下语句取决于返回的数据。
//Controller
App.controller('LeafyVeggieController', function(HealthyFoodService) {
//Ideally I just'like to do something like below instead of calling `.then()` and registering callbacks.
var leafyVeggies = FoodService.getLeafyVeggies();
//leafyVeggies is undefined because data is not available yet;
});
//Another service depending on HealthyFoodService- similar scenario
App.factory('LeafyVeggieReportService', function(HealthyFoodService) {
function generateLeafyVeggieReport() {
var name = 'spinach';
var veggieInfo = HealthyFoodService.getLeafyVeggieByName(spinach);
//veggieInfo is undefined
//logic that need data.
});
我之前的方法
以下是我以前如何部分工作但我不满意每次我需要数据时使用.then()。(即使在相同的服务中)
App.factory('HealthyFoodService', function($resource, $q) {
//resource variables;
function getLeafyVeggies() {
return $q.all([veggiesPromise]).then(function(data) {
//logic
return leafyVeggies;
});
}
function getLeafyVeggieByName() {
var leafyVeggies = getLeafyVeggies().then(function(data) {
return data;
}
//some logic
//still causes issues when called from another service because above call doesn't get the data right away.
}
return {
getLeafyVeggies: getLeafyVeggies,
getLeafyVeggieByName: getLeafyVeggieByName
}
//controller
App.controller('LeafyVeggieController', function(HealthyFoodService) {
var leafyVeggies = HealthyFoodService.getLeafyVeggies().then(function(data) {
return data;
});
//controller related logic
});
更新
我也在使用ui-router,所以我知道我可以在resolve:{}
中使用$stateProvider
将数据直接注入控制器。问题是当我从另一个服务或同一服务中的另一个函数发出请求而不必使用.then()
时,如何获取数据。
在依赖于我的数据服务的客户端服务中使用$q.all([])
已经为我完成了诀窍。每当我处于开始处理逻辑之前需要存在所有数据的情况下,我就使用了$q.all([])
。
我仍然必须在我的客户端上使用.then(),但是通过使用$q.all([])
,我仍然可以在不违反任何异步原则的情况下轻微模拟同步流。
答案 0 :(得分:0)
我不认为这是可能的。您需要等待的网络操作存在固有延迟。不这样做会导致应用程序在数据可用之前继续。
话虽如此,大多数本机模型绑定操作将隐式等待promises,因此如果在传递给视图之前不需要进一步操作数据,则不需要.then
。您还可以使用transformResponse
的{{1}}方法来帮助解决此问题。
另一种选择可能是将复杂性转移到路由配置中的ngResource
方法。在这种情况下,您将处理解析中的resolve
并将已解析的数据传递给控制器。这将使您的控制器更清洁,但仍需要您解决路由配置中的承诺。
答案 1 :(得分:0)
尝试让服务保留您的数据并让控制器引用该数据,而不是尝试将其传递给您的控制器范围。像这样解决服务中的承诺:
App.factory("HealthyFoodService", function($resource,$q) {
var service = {};
service.data = {
fruitsData: [],
veggiesData: []
}
$resource('api/food/fruits', {}).query().$promise.then(function(data) {
$service.data.fruitsData = data[0];
$service.data.veggiesData = data[1];
})
service.getCitrusFruits = function() {
var citrusFruits;
// Perform some logic on service.data.fruitsData
return citrusFruits;
}
return service;
})
在您的控制器中,您可以像这样与服务对话:
App.controller("FruitController", function($scope, HealthyFoodService) {
// Now you can access the service data directly via $scope.hfs.fruitsData
$scope.hfs = HealthyFoodService.data;
// Or we can create a local copy of the data using the getCitrusFruits function
$scope.citrusFruits = HealthyFoodService.getCitrusFruits();
})