3

我正在开发一个复杂的 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来实现相同的目标?

4

1 回答 1

3

您正在做的是修改全局状态,并将更改更改为$q,这种 AOP 可以很容易地与第三方模块插件产生问题,我认为这很危险,它不会让您自己确定更改的范围,更不用说速度惩罚。

我认为更好的

在我看来,您真正想要的是一种进行资源管理的方法,一种try(resource)来自 Java、using(C# 或withPython 的方法。不幸的是,唯一具有此功能的 Promise 实现是 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;
 });

如果您将显示/隐藏逻辑、事件挂钩、作用域变量或输入参数放在加载函数中(我将其保留为个人喜好,只需在它达到零时做一些事情,当它达到一时) - 它将显示/隐藏加载屏幕。

于 2014-05-19T07:57:38.037 回答