我有一个具有一些缓存逻辑的服务方法:
model.fetchMonkeyHamById = function(id)
{
var that = this;
var deferred = $q.defer();
if( that.data.monkeyHam )
{
deferred.resolve( that.data.monkeyHam );
return deferred.promise;
} else {
return this.service.getById( id).then( function(result){
that.data.monkeyHam = result.data;
});
}
};
我知道如何使用$ httpBackend强制返回模拟数据。现在,当我明确设置数据时,如何强制它解析(然后测试结果)?
我想在控制器中测试结果then()函数:
MonkeyHamModel.fetchMonkeyHamById(this.monkeyHamId).then( function() {
$scope.currentMonkeyHam = MonkeyHamModel.data.monkeyHam;
});
然后我的测试我想显式设置数据(所以它从内存加载“缓存”而不是httpBackend)
MonkeyHamModel.data.monkeyHam = {id:'12345'};
MonkeyHamModel.fetchMonkeyHamById( '12345');
// How to "flush" the defer right here like I would have flushed httpBackend?
expect( $scope.currentMonkeyHam.id ).toEqual('12345'); //fails because it is not defined yet since the $q never resolved
其中$ scope只是我的控制器的范围,但为简洁起见,这里称为$ scope。
更新
建议的答案不起作用。我需要函数来返回一个promise,而不是一个promise的结果:
model._monkeyHams = {} // our cache
model.fetchMonkeyHamById = function(id){
return model.monkeyHams[id] || // get from cache or
(model.monkeyHams[id] = this.service.getById(id).then(function(result){
return result.data;
}));
};
以下要求您已经触摸过服务器。我在前端创建了一个模型(currentMonkeyHam)或者其他什么,并且在第一次POST(不必要的GET请求)之后不要加载它。我只是使用当前的模型。所以这不起作用,它需要至少一次到服务器。因此,你可以看到为什么我创建了自己的延迟。我想使用当前的模型数据,或者如果我们没有它,则从服务器获取它。我需要两种途径来回报承诺。
var cache = null;
function cachedRequest(){
return cache || (cache = actualRequest())
}
答案 0 :(得分:3)
您的代码具有延迟反模式,这使得它变得复杂 - 特别是因为您隐式地使用它来抑制错误。此外,缓存逻辑也存在问题,因为如果在收到响应之前发出了多个请求,您最终可能会发出多个请求。
你过分暗示它 - 只是缓存承诺:
model._monkeyHams = {} // our cache
model.fetchMonkeyHamById = function(id){
return model.monkeyHams[id] || // get from cache or
(model.monkeyHams[id] = this.service.getById(id).then(function(result){
return result.data;
}));
};
在你的情况下,你将所有ID缓存为同一个东西,缓存承诺的一般模式是这样的:
var cache = null;
function cachedRequest(){
return cache || (cache = actualRequest())
}
创造延迟是乏味而坦率的 - 不是很有趣;)
答案 1 :(得分:1)
您可以使用setTimeout (or $timeout)
来解决承诺。
您可以将代码修改为 -
model.fetchMonkeyHamById = function(id)
{
var that = this;
var deferred = $q.defer();
if( that.data.monkeyHam )
{
setTimeout(function() {
deferred.resolve(that.data.monkeyHam);
}, 100);
return deferred.promise;
} else {
return this.service.getById( id).then( function(result){
that.data.monkeyHam = result.data;
});
}
};
修改强>
按照本杰明的建议修改 -
使用$rootScope.digest()
- 代码应该是这样的
MonkeyHamModel.data.monkeyHam = {id:'12345'};
MonkeyHamModel.fetchMonkeyHamById( '12345');
$rootScope.digest();
答案 2 :(得分:0)
我们在代码库中做了类似的事情,但是我们没有使用状态不断改变的对象,而是使用了更像传统存储库的东西。
someInjectedRepoistory.getMonkeyHamModel().then(x => $scope.monkeyHam = x);
Repository{
getMonkeyHamModel() {
var deferred = $q.defer();
if( this.cache.monkeyHam )
{
deferred.resolve( this.cache.monkeyHam );
} else {
return this.service.getById( id).then( function(result){
this.cache.monkeyHam = result.data;
});
}
return deferred.promise
}
}
返回完成的延期没有问题。这是延期目的的一部分,无论何时或如何解决它们都不重要,它们会为您处理所有这些。
至于你的测试,我们会做这样的事情。
testGetFromService(){
someInjectedRepoistory.getMonkeyHamModel().then(x => verifyMonkeyHam(x));
verifyHttpBackEndGetsCalled()
}
testGetFromCache(){
someInjectedRepoistory.getMonkeyHamModel().then(x => verifyMonkeyHam(x));
verifyHttpBackEndDoesNotGetCalled()
}