计算angularjs中等待$ q的承诺

时间:2014-05-19 03:38:32

标签: javascript angularjs promise q angular-promise

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

1 个答案:

答案 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;
 });

如果你在你的加载函数中放置你的显示/隐藏逻辑,事件挂钩,范围变量或输入参数(我将这个留给个人喜好,只需要做某事当它达到零时,当它击中一个时) - 它将显示/隐藏加载屏幕。