我正在开发一个复杂的angularjs应用程序。复杂含义单个视图可以从代码的不同位置查询几个不同的数据源,例如,页面部分控制器或指令控制器。有时,其中一些请求可能需要较长时间才能解决,从而使视图处于某种无效状态。 我希望能够显示某种"数据加载"横幅并禁用用户与视图的交互,直到所有承诺都已解决。这适用于使用$ q.all()函数的简单屏幕。 但是,构建应用程序的方式更多的是以业务逻辑为中心,而不是以数据源为中心。因此,对于更复杂的屏幕,没有一个地方可以自然地获得所有承诺。在代码中创建这样的地方似乎很麻烦。
我已经提出了这个解决方案:
angular.module('myApp').service('qConfigurer', function ($q) {
var pending = 0;
var origDefer = $q.defer;
$q.defer = function() {
pending++;
var result = origDefer.apply(arguments);
var origResolve = result.resolve;
var origReject = result.reject;
result.resolve = function() {
pending--;
return origResolve.apply(arguments);
};
result.reject = function() {
pending--;
return origReject.apply(arguments);
};
return result;
};
$q.pending = function() {
return pending;
};
return {};
});
是否有更少的 hacky 来实现同样的目标?
答案 0 :(得分:3)
您正在做的是修改全局状态,并将修改变为$q
,这种AOP可以很容易地创建第三方模块插件的问题,我相信它很危险,它不会让你自己调整范围,更不用说速度惩罚了。
在我看来,您真正想要的是一种资源管理方式,一种来自Java的try(resource)
,来自C#的using(
或来自Python的with
。不幸的是,唯一的承诺实现是Bluebird,我们在这里使用$ q,所以让我们做一个:)
所以,我们想要一个对promise做范围的函数,无论结果是什么,都会从一个计数器中减少一个 - 在我们的例子中,我们的资源形成一个semaphore。
function loading(fn){ // takes a function that returns a promise, put in a service
var args = Array.prototype.slice.call(arguments,1);
return $q.when().then(function(){
loading.counter++; // signal the scope somehow, either by having the counter
// on the scope and accepting it as a param, by a watcher or
// with an emit
}).then(function(){
return fn.apply(null, args); // can add context param if you want for `this`
}).finally(function(){
loading.counter--; // signal just like with the above
});
}
loading.counter = 0;
然后,用法变为:
// you can use it like this
loading(function(){
return myService.apiCall(...);
}).then(function(result){
$scope.a = result;
});
// or like this
loading(myService.apiCall,...).then(function(result){
$scope.b = result;
});
如果你在你的加载函数中放置你的显示/隐藏逻辑,事件挂钩,范围变量或输入参数(我将这个留给个人喜好,只需要做某事当它达到零时,当它击中一个时) - 它将显示/隐藏加载屏幕。