准确跟踪$ q.defer调用的Angular的active / done promise

时间:2014-10-14 23:34:03

标签: javascript angularjs profiling promise angular-promise

对于我的角度应用程序,我正在尝试跟踪已处理的承诺数量以及仍需要处理的数量。

我的代码使用包装器方法修饰$ q,无论何时启动操作并且只要一个操作完成,更新一个简单的计数器似乎很简单:

  angular.module('DuckieTV',[])
  .config(function($provide) {
  var count = window.promiseStats = { 
    open: 0,
    done: 0 
  };
  $provide.decorator('$q', function($delegate) {

    function decorate(p) {
      p._then = p.then;
      p.then = function(thenFn, errFn, notifyFn) {
       count.open++;
       return p._then(function() { 
            count.done++;
            if(thenFn) return thenFn.apply(this,arguments)
        }, function() {
          count.done++;
          if(errFn) return errFn.apply(this,arguments)
        }, notifyFn);
      };

      p._finally = p.finally;
      p.finally = function(callback) {
        count.done++;
        p._finally(callback)
      }

      p._catch = p.catch;
      p.catch = function(callback) {
        count.done++;
        p._catch(callback)
      }
      return p;
    }

    var d = $delegate.defer;
    $delegate.defer = function() {
      var deferred = d();
      decorate(deferred.promise);
      return deferred;
    };

    return $delegate;
  });
})

当我注意到已启动/已完成的承诺之间的差异时,乐趣就开始了。在执行大量延期操作几分钟后,它可以成为一个>总体比例为15%。

控制台输出示例:

promiseStats
Object {open: 99, done: 95}
Math.floor(promiseStats.done / promiseStats.open * 100);
94

经过一些工作(导入操作)

promiseStats;
Object {open: 185, done: 172}
Math.floor(promiseStats.done / promiseStats.open * 100);
92

我的实际问题:任何人都可以告诉我,如果我错过了这个实现的东西吗?

据我所知,我没有错过任选的实现和我通过的所有承诺。然后正确编码

2 个答案:

答案 0 :(得分:2)

  

任何人都可以告诉我,如果我错过了这个实现的内容吗?

我可以看到您的实施有两个问题:

  • 您的装饰catchinvoke方法只增加done次,而不是open次。由于您没有看到这一点,我猜您还没有在您的代码中使用过。
  • 通过向每个onfail调用引入.then()处理程序,您隐式捕获所有错误并严重hurt control flow。您可以通过将以下行附加到处理程序代码来解决此问题:

     …
     else throw arguments[0];
    
  

我注意到已启动/已完成的承诺之间的差异

我不认为这是由你的计数器实施引起的。相反,你真的在你的代码中有一些永远未决的承诺;即从未解决的延期。例如,这可能是由使用deferred antipattern的代码中的错误引起的。

  

我的代码使用包装器方法装饰$q,无论何时启动操作并且只要一个操作完成,它就会更新一个简单的计数器,这看起来很简单:

事实上,只要连接侦听器,它就会更新计数器,即调用then / catch / finally方法。我建议一个更简单的实现,依靠创建,甚至不需要覆盖这些方法:

$provide.decorator('$q', function($delegate) {
    var defer = $delegate.defer;
    $delegate.defer = function() {
        var deferred = defer();
        count.open++;
        deferred.promise.finally(function() {
            count.done++;
        });
        return deferred;
    };
    return $delegate;
});

答案 1 :(得分:1)

情节变浓。

我一直在根据@Bergi的优秀代码进行分析和跟踪代码,并注意到#31保持开放,所以我在那里添加了一个调试语句:

.config(function($provide) {
    var count = window.promiseStats = {
        open: 0,
        done: 0,
        promises: {}
    };
    $provide.decorator('$q', function($delegate) {


    var defer = $delegate.defer;
        $delegate.defer = function() {

            count.open++;
            var traceId = count.open;
            if(traceId == 31) { 
                debugger;
            }
            var deferred = count.promises[traceId] = defer();
            console.timeline('promise ' +traceId);
            console.profile('promise '+traceId);

            deferred.promise.finally(function() {
                count.done++;
                console.timelineEnd('promise ' +traceId);
                console.profileEnd('promise '+traceId);
                delete count.promises[traceId];    
            });
            return deferred;
        };
        return $delegate;
    });
})

这让我直接进入了一个角度核心模板请求,似乎有一种不同类型的承诺处理。

enter image description here

我还在努力评估这是否是一个问题。因为它似乎注销了一些专门的代码。