链接承诺,或一个承诺触发另一个
Posted
技术标签:
【中文标题】链接承诺,或一个承诺触发另一个【英文标题】:linking promises, or one promise triggers another 【发布时间】:2013-11-25 04:02:30 【问题描述】:我正在构建一个天气应用程序,我首先必须获取用户的位置,然后发出获取天气的请求。
所以我有一个GeolocationService
和一个WeatherService
。我的WeatherService
目前正在调用Geolocation
服务。如何让WeatherService
等到它有来自GeolocationService
的结果后再发出HTTP 请求?
【问题讨论】:
真的不认为你需要在$apply
中包含resolve 和reject,不知道是什么想法
@charlietfl - 我试过不将解析包装在 $apply 中,但没有它就不会触发 .then。
【参考方案1】:
你只需要在你的服务中使用承诺,然后链接承诺。对于示例,我有一个 user.company_id 的用户,如果我想获得公司的名称,我必须等待用户加载。和你的情况一样。
这是我的服务:
angular.module('UserService', [])
.factory('UserService', function($q , $http, $rootScope,$timeout)
var currentUserPromise = null;
var currentCompanyPromise = null;
return
getCurrentUser: function()
if (currentUserPromise === null)
var config = ;
config.cache = true;
config.method = "GET";
config.url = "users/get_current_user";
currentUserPromise = $http(config)
.then(function(response)
if (typeof response.data === 'object')
return response.data.user;
else
// invalid response
return $q.reject(response.data);
, function(response)
// something went wrong
return $q.reject(response.data);
);
return currentUserPromise;
,
getCurrentCompany: function(company_id)
if (currentCompanyPromise === null)
var config = ;
var company = ;
company.id = company_id;
config.cache = true;
config.method = "GET";
config.url = "/companies/show";
config.params = company;
currentCompanyPromise = $http(config)
.then(function(response)
if (typeof response.data === 'object')
return response.data.company;
else
// invalid response
return $q.reject(response.data);
, function(response)
// something went wrong
return $q.reject(response.data);
);
return currentCompanyPromise;
;
);
在我的控制器中,我这样使用:
控制器:
var promiseCurrentUser = UserService.getCurrentUser();
promiseCurrentUser.then(function(user)
$scope.currentUser = user;
UserService.getCurrentCompany(user.company_id).then(function(company)
$scope.companyName = company.name;
);
);
promise 最酷的地方在于它是一次性解决的。
希望对你有帮助。
【讨论】:
【参考方案2】:您的GeolocationService
的函数getLatLon
正在返回一个承诺。无需使用new
运算符调用它。
您的getWeather
函数应该类似于:
app.factory("WeatherService", function ($q,$http,$rootScope, GeolocationService)
return
getWeather: function()
var weather;
return GeolocationService.getLatLon().then(
function (loc)
var lat= loc.lat || 37.4568202221774,
lon= loc.lon || -122.201366838789 ;
var units = '';
var url = 'http://api.openweathermap.org/data/2.5/forecast/daily?lat='+lat+'&lon='+lon+'&units='+units+'&callback=JSON_CALLBACK';
return $http.jsonp(url)
.success(function(data)
weather=data;
return weather;
)
.error(function(err)
weather=err;
return err;
);
);
在这里,我们首先调用GeolocationService.getLatLon()
来获得承诺。然后我们将我们的处理链作为一个链链接到它。成功函数会得到position
,你在这里解决它deferred.resolve(position);
。
此外,您不需要将resolve
和reject
包装在$apply
中。因此,您的GeoLocationService
可以简化为:
if(!window.navigator)
deferred.reject(new Error("Geolocation not available"));
else
$window.navigator.geolocation.getCurrentPosition(function(position)
deferred.resolve(position);
, function(error)
deferred.reject(error);
);
【讨论】:
感谢@musically_ut,如果不包装延迟,.then 不会被触发。此外,从成功语句返回weather
不会将 weather
传递给范围。我的范围只是$scope.weather = WeatherModel.getWeather();
您使用的是哪个版本的 Angular?最新版本 (1.2) makes promises A+ compliant,它们应该在不需要额外的 $digest
循环的情况下触发。 weather
是 传递给范围,但作为一个承诺。如果您的任何操作依赖于weather
的值,请将它们包含在一个链中:$scope.weather.then(function (weatherData) ... )
。或者在 then:
WeatherModel.getWeather().then(function (weatherData) $scope.weather = weatherData; ) 中分配 weather
。不过,这两者都是异步。
这就解释了,我正在使用 v 1.5,希望在即将发布的版本中升级。我尝试了链接“then”的建议,但收到错误“无法调用未定义匿名函数的方法”。
刚刚又测试了一遍,我的电话里肯定有()
。以上是关于链接承诺,或一个承诺触发另一个的主要内容,如果未能解决你的问题,请参考以下文章